{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Basic Setup 基本设置"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Starting Portfolio Value: 10000.00\n",
      "Final Portfolio Value: 10000.00\n"
     ]
    }
   ],
   "source": [
    "from __future__ import (absolute_import, division, print_function,\n",
    "                        unicode_literals)\n",
    "\n",
    "import backtrader as bt\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    cerebro = bt.Cerebro()\n",
    "\n",
    "    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())\n",
    "\n",
    "    cerebro.run()\n",
    "\n",
    "    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Setting the Cash 设置现金\n",
    "cerebro.broker.setcash(100000.0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Starting Portfolio Value: 100000.00\n",
      "Final Portfolio Value: 100000.00\n"
     ]
    }
   ],
   "source": [
    "from __future__ import (absolute_import, division, print_function,\n",
    "                        unicode_literals)\n",
    "\n",
    "import backtrader as bt\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    cerebro = bt.Cerebro()\n",
    "    cerebro.broker.setcash(100000.0)\n",
    "\n",
    "    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())\n",
    "\n",
    "    cerebro.run()\n",
    "\n",
    "    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 添加数据馈送\n",
    "1. data = bt.feeds.....(dataname=df,。。。。)\n",
    "2. cerebro.adddata(data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Starting Portfolio Value: 100000.00\n",
      "Final Portfolio Value: 100000.00\n"
     ]
    }
   ],
   "source": [
    "from __future__ import (absolute_import, division, print_function,\n",
    "                        unicode_literals)\n",
    "\n",
    "import datetime  # For datetime objects\n",
    "import os.path  # To manage paths\n",
    "import sys  # To find out the script name (in argv[0])\n",
    "\n",
    "# Import the backtrader platform\n",
    "import backtrader as bt\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    # Create a cerebro entity\n",
    "    cerebro = bt.Cerebro()\n",
    "\n",
    "    # Datas are in a subfolder of the samples. Need to find where the script is\n",
    "    # because it could have been called from anywhere\n",
    "    # modpath = os.path.dirname(os.path.abspath(sys.argv[0]))\n",
    "    modpath = 'C:\\\\Users\\\\bww\\gitee\\\\stock\\\\backtrader'\n",
    "    datapath = os.path.join(modpath, '000001.SS.csv')\n",
    "\n",
    "    # Create a Data Feed\n",
    "    data = bt.feeds.YahooFinanceCSVData(\n",
    "        dataname=datapath,\n",
    "        # Do not pass values before this date\n",
    "        fromdate=datetime.datetime(2000, 1, 1),\n",
    "        # Do not pass values after this date\n",
    "        todate=datetime.datetime(2000, 12, 31),\n",
    "        reverse=False)\n",
    "\n",
    "    # Add the Data Feed to Cerebro\n",
    "    cerebro.adddata(data)\n",
    "\n",
    "    # Set our desired cash start\n",
    "    cerebro.broker.setcash(100000.0)\n",
    "\n",
    "    # Print out the starting conditions\n",
    "    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())\n",
    "\n",
    "    # Run over everything\n",
    "    cerebro.run()\n",
    "\n",
    "    # Print out the final result\n",
    "    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "modpath = 'C:\\\\Users\\\\bww\\gitee\\\\stock\\\\backtrader'\n",
    "datapath = os.path.join(modpath, 'sh.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'C:\\\\Users\\\\bww\\\\gitee\\\\stock\\\\backtrader\\\\sh.csv'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "datapath \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Our First Strategy\n",
    "```\n",
    "class TestStrategy(bt.Strategy):\n",
    "    def log(self, txt, dt=None):\n",
    "    def __init__(self):\n",
    "    def next(self):\n",
    "```\n",
    "cerebro.addstrategy(TestStrategy)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Starting Portfolio Value: 1000000.00\n",
      "2023-06-19, Close, 3255.81\n",
      "2023-06-20, Close, 3240.36\n",
      "2023-06-21, Close, 3197.90\n",
      "2023-06-26, Close, 3150.62\n",
      "2023-06-27, Close, 3189.44\n",
      "2023-06-28, Close, 3189.38\n",
      "2023-06-29, Close, 3182.38\n",
      "2023-06-30, Close, 3202.06\n",
      "2023-07-03, Close, 3243.98\n",
      "2023-07-04, Close, 3245.35\n",
      "2023-07-05, Close, 3222.95\n",
      "2023-07-06, Close, 3205.57\n",
      "2023-07-07, Close, 3196.61\n",
      "2023-07-10, Close, 3203.70\n",
      "2023-07-11, Close, 3221.37\n",
      "2023-07-12, Close, 3196.13\n",
      "2023-07-13, Close, 3236.48\n",
      "2023-07-14, Close, 3237.70\n",
      "2023-07-17, Close, 3209.63\n",
      "2023-07-18, Close, 3197.82\n",
      "2023-07-19, Close, 3198.84\n",
      "2023-07-20, Close, 3169.52\n",
      "2023-07-21, Close, 3167.75\n",
      "2023-07-24, Close, 3164.16\n",
      "2023-07-25, Close, 3231.52\n",
      "2023-07-26, Close, 3223.03\n",
      "2023-07-27, Close, 3216.67\n",
      "2023-07-28, Close, 3275.93\n",
      "2023-07-31, Close, 3291.04\n",
      "2023-08-01, Close, 3290.95\n",
      "2023-08-02, Close, 3261.69\n",
      "2023-08-03, Close, 3280.46\n",
      "2023-08-04, Close, 3288.08\n",
      "2023-08-07, Close, 3268.83\n",
      "2023-08-08, Close, 3260.62\n",
      "2023-08-09, Close, 3244.49\n",
      "2023-08-10, Close, 3254.56\n",
      "2023-08-11, Close, 3189.25\n",
      "2023-08-14, Close, 3178.43\n",
      "2023-08-15, Close, 3176.18\n",
      "2023-08-16, Close, 3150.13\n",
      "2023-08-17, Close, 3163.74\n",
      "2023-08-18, Close, 3131.95\n",
      "2023-08-21, Close, 3092.98\n",
      "2023-08-22, Close, 3120.33\n",
      "2023-08-23, Close, 3078.40\n",
      "2023-08-24, Close, 3082.24\n",
      "2023-08-25, Close, 3064.07\n",
      "2023-08-28, Close, 3098.64\n",
      "2023-08-29, Close, 3135.89\n",
      "2023-08-30, Close, 3137.14\n",
      "2023-08-31, Close, 3119.88\n",
      "2023-09-01, Close, 3133.25\n",
      "2023-09-04, Close, 3177.06\n",
      "2023-09-05, Close, 3154.37\n",
      "2023-09-06, Close, 3158.08\n",
      "2023-09-07, Close, 3122.35\n",
      "2023-09-08, Close, 3116.72\n",
      "2023-09-11, Close, 3142.78\n",
      "2023-09-12, Close, 3137.06\n",
      "2023-09-13, Close, 3123.07\n",
      "2023-09-14, Close, 3126.55\n",
      "2023-09-15, Close, 3117.74\n",
      "2023-09-18, Close, 3125.93\n",
      "2023-09-19, Close, 3124.96\n",
      "2023-09-20, Close, 3108.57\n",
      "2023-09-21, Close, 3084.70\n",
      "2023-09-22, Close, 3132.43\n",
      "2023-09-25, Close, 3115.61\n",
      "2023-09-26, Close, 3102.27\n",
      "2023-09-27, Close, 3107.32\n",
      "2023-09-28, Close, 3110.48\n",
      "2023-10-09, Close, 3096.92\n",
      "2023-10-10, Close, 3075.24\n",
      "2023-10-11, Close, 3078.96\n",
      "2023-10-12, Close, 3107.90\n",
      "2023-10-13, Close, 3088.10\n",
      "2023-10-16, Close, 3073.81\n",
      "2023-10-17, Close, 3083.50\n",
      "2023-10-18, Close, 3058.71\n",
      "2023-10-19, Close, 3005.39\n",
      "2023-10-20, Close, 2983.06\n",
      "2023-10-23, Close, 2939.29\n",
      "2023-10-24, Close, 2962.24\n",
      "2023-10-25, Close, 2974.11\n",
      "2023-10-26, Close, 2988.30\n",
      "2023-10-27, Close, 3017.78\n",
      "2023-10-30, Close, 3021.55\n",
      "2023-10-31, Close, 3018.77\n",
      "2023-11-01, Close, 3023.08\n",
      "2023-11-02, Close, 3009.41\n",
      "2023-11-03, Close, 3030.80\n",
      "2023-11-06, Close, 3058.41\n",
      "2023-11-07, Close, 3057.27\n",
      "2023-11-08, Close, 3052.37\n",
      "2023-11-09, Close, 3053.28\n",
      "2023-11-10, Close, 3038.97\n",
      "2023-11-13, Close, 3046.53\n",
      "2023-11-14, Close, 3056.07\n",
      "2023-11-15, Close, 3072.83\n",
      "2023-11-16, Close, 3050.93\n",
      "2023-11-17, Close, 3054.37\n",
      "2023-11-20, Close, 3068.32\n",
      "2023-11-21, Close, 3067.93\n",
      "2023-11-22, Close, 3043.61\n",
      "2023-11-23, Close, 3061.86\n",
      "2023-11-24, Close, 3040.97\n",
      "2023-11-27, Close, 3031.70\n",
      "2023-11-28, Close, 3038.55\n",
      "2023-11-29, Close, 3021.69\n",
      "2023-11-30, Close, 3029.67\n",
      "2023-12-01, Close, 3031.64\n",
      "2023-12-04, Close, 3022.91\n",
      "2023-12-05, Close, 2972.30\n",
      "2023-12-06, Close, 2968.93\n",
      "2023-12-07, Close, 2966.21\n",
      "2023-12-08, Close, 2969.56\n",
      "2023-12-11, Close, 2991.44\n",
      "2023-12-12, Close, 3003.44\n",
      "2023-12-13, Close, 2968.76\n",
      "2023-12-14, Close, 2958.99\n",
      "2023-12-15, Close, 2942.56\n",
      "2023-12-18, Close, 2930.80\n",
      "2023-12-19, Close, 2932.39\n",
      "2023-12-20, Close, 2902.11\n",
      "2023-12-21, Close, 2918.72\n",
      "2023-12-22, Close, 2914.77\n",
      "2023-12-25, Close, 2918.81\n",
      "2023-12-26, Close, 2898.88\n",
      "2023-12-27, Close, 2914.61\n",
      "2023-12-28, Close, 2954.70\n",
      "2023-12-29, Close, 2974.94\n",
      "2024-01-02, Close, 2962.28\n",
      "2024-01-03, Close, 2967.25\n",
      "2024-01-04, Close, 2954.35\n",
      "2024-01-05, Close, 2929.18\n",
      "2024-01-08, Close, 2887.54\n",
      "2024-01-09, Close, 2893.25\n",
      "2024-01-10, Close, 2877.70\n",
      "2024-01-11, Close, 2886.65\n",
      "2024-01-12, Close, 2881.98\n",
      "2024-01-15, Close, 2886.29\n",
      "2024-01-16, Close, 2893.99\n",
      "2024-01-17, Close, 2833.61\n",
      "2024-01-18, Close, 2845.78\n",
      "2024-01-19, Close, 2832.28\n",
      "2024-01-22, Close, 2756.34\n",
      "2024-01-23, Close, 2770.98\n",
      "2024-01-24, Close, 2820.77\n",
      "2024-01-25, Close, 2906.11\n",
      "2024-01-26, Close, 2910.22\n",
      "2024-01-29, Close, 2883.36\n",
      "2024-01-30, Close, 2830.53\n",
      "2024-01-31, Close, 2788.55\n",
      "2024-02-01, Close, 2770.74\n",
      "2024-02-02, Close, 2730.15\n",
      "2024-02-05, Close, 2702.19\n",
      "2024-02-06, Close, 2789.49\n",
      "2024-02-07, Close, 2829.70\n",
      "2024-02-08, Close, 2865.90\n",
      "2024-02-19, Close, 2910.54\n",
      "2024-02-20, Close, 2922.73\n",
      "2024-02-21, Close, 2950.96\n",
      "2024-02-22, Close, 2988.36\n",
      "2024-02-23, Close, 3004.88\n",
      "2024-02-26, Close, 2977.02\n",
      "2024-02-27, Close, 3015.48\n",
      "2024-02-28, Close, 2957.85\n",
      "2024-02-29, Close, 3015.17\n",
      "2024-03-01, Close, 3027.02\n",
      "2024-03-04, Close, 3039.31\n",
      "2024-03-05, Close, 3047.79\n",
      "2024-03-06, Close, 3039.93\n",
      "2024-03-07, Close, 3027.40\n",
      "2024-03-08, Close, 3046.02\n",
      "2024-03-11, Close, 3068.46\n",
      "2024-03-12, Close, 3055.94\n",
      "2024-03-13, Close, 3043.83\n",
      "2024-03-14, Close, 3038.23\n",
      "2024-03-15, Close, 3054.64\n",
      "2024-03-18, Close, 3084.93\n",
      "2024-03-19, Close, 3062.76\n",
      "2024-03-20, Close, 3079.69\n",
      "2024-03-21, Close, 3077.11\n",
      "2024-03-22, Close, 3048.03\n",
      "2024-03-25, Close, 3026.31\n",
      "2024-03-26, Close, 3031.48\n",
      "2024-03-27, Close, 2993.14\n",
      "2024-03-28, Close, 3010.66\n",
      "2024-03-29, Close, 3041.17\n",
      "2024-04-01, Close, 3077.38\n",
      "2024-04-02, Close, 3074.96\n",
      "2024-04-03, Close, 3069.30\n",
      "2024-04-08, Close, 3047.05\n",
      "2024-04-09, Close, 3048.54\n",
      "2024-04-10, Close, 3027.33\n",
      "2024-04-11, Close, 3034.25\n",
      "2024-04-12, Close, 3019.47\n",
      "2024-04-15, Close, 3057.38\n",
      "2024-04-16, Close, 3007.07\n",
      "2024-04-17, Close, 3071.38\n",
      "2024-04-18, Close, 3074.23\n",
      "2024-04-19, Close, 3065.26\n",
      "2024-04-22, Close, 3044.59\n",
      "2024-04-23, Close, 3021.98\n",
      "2024-04-24, Close, 3044.82\n",
      "2024-04-25, Close, 3052.90\n",
      "2024-04-26, Close, 3088.64\n",
      "2024-04-29, Close, 3113.04\n",
      "2024-04-30, Close, 3104.82\n",
      "2024-05-06, Close, 3140.72\n",
      "2024-05-07, Close, 3147.74\n",
      "2024-05-08, Close, 3128.48\n",
      "2024-05-09, Close, 3154.32\n",
      "2024-05-10, Close, 3154.55\n",
      "2024-05-13, Close, 3148.02\n",
      "2024-05-14, Close, 3145.77\n",
      "2024-05-15, Close, 3119.90\n",
      "2024-05-16, Close, 3122.40\n",
      "2024-05-17, Close, 3154.03\n",
      "2024-05-20, Close, 3171.15\n",
      "2024-05-21, Close, 3157.97\n",
      "2024-05-22, Close, 3158.54\n",
      "2024-05-23, Close, 3116.39\n",
      "2024-05-24, Close, 3088.87\n",
      "2024-05-27, Close, 3124.04\n",
      "2024-05-28, Close, 3109.57\n",
      "2024-05-29, Close, 3111.02\n",
      "2024-05-30, Close, 3091.68\n",
      "2024-05-31, Close, 3086.81\n",
      "2024-06-03, Close, 3078.49\n",
      "2024-06-04, Close, 3091.20\n",
      "2024-06-05, Close, 3065.40\n",
      "2024-06-06, Close, 3048.79\n",
      "2024-06-07, Close, 3051.28\n",
      "2024-06-11, Close, 3028.04\n",
      "2024-06-12, Close, 3037.47\n",
      "2024-06-13, Close, 3028.92\n",
      "2024-06-14, Close, 3032.63\n",
      "2024-06-17, Close, 3015.89\n",
      "Final Portfolio Value: 1000000.00\n"
     ]
    }
   ],
   "source": [
    "from __future__ import (absolute_import, division, print_function,\n",
    "                        unicode_literals)\n",
    "\n",
    "import datetime  # For datetime objects\n",
    "import os.path  # To manage paths\n",
    "import sys  # To find out the script name (in argv[0])\n",
    "\n",
    "# Import the backtrader platform\n",
    "import backtrader as bt\n",
    "\n",
    "\n",
    "# ===================================Create a Stratey============================================\n",
    "class TestStrategy(bt.Strategy):\n",
    "    # 定义了一个名为 log 的方法，该方法接受两个参数：txt 和 dt。\n",
    "    # txt 是要记录的文本信息，dt 是可选的日期参数，默认为 None。\n",
    "    def log(self, txt, dt=None):\n",
    "        ''' Logging function for this strategy'''\n",
    "        # 如果 dt 已经被赋值且不为 None，则 dt 保持不变。\n",
    "        # 如果 dt 未被赋值或为 None，则 dt 被赋值为 self.datas[0].datetime.date(0)。        \n",
    "        dt = dt or self.datas[0].datetime.date(0)\n",
    "        print('%s, %s' % (dt.isoformat(), txt))\n",
    "\n",
    "    def __init__(self):\n",
    "        # Keep a reference to the \"close\" line in the data[0] dataseries\n",
    "        self.dataclose = self.datas[0].close\n",
    "\n",
    "    def next(self):\n",
    "        # Simply log the closing price of the series from the reference\n",
    "        self.log('Close, %.2f' % self.dataclose[0])\n",
    "# ==============================================================================================\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    # Create a cerebro entity\n",
    "    cerebro = bt.Cerebro()\n",
    "\n",
    "    # Add a strategy\n",
    "    cerebro.addstrategy(TestStrategy)\n",
    "\n",
    "    # Datas are in a subfolder of the samples. Need to find where the script is\n",
    "    # because it could have been called from anywhere\n",
    "    # modpath = os.path.dirname(os.path.abspath(sys.argv[0]))\n",
    "    modpath = 'C:\\\\Users\\\\bww\\gitee\\\\stock\\\\backtrader'\n",
    "    datapath = os.path.join(modpath, '000001.SS.csv')\n",
    "\n",
    "    # Create a Data Feed\n",
    "    data = bt.feeds.YahooFinanceCSVData(\n",
    "        dataname=datapath,\n",
    "        # Do not pass values before this date\n",
    "        fromdate=datetime.datetime(2000, 1, 1),\n",
    "        # Do not pass values before this date\n",
    "        todate=datetime.datetime(2024, 12, 31),\n",
    "        # Do not pass values after this date\n",
    "        reverse=False)\n",
    "\n",
    "    # Add the Data Feed to Cerebro\n",
    "    cerebro.adddata(data)\n",
    "\n",
    "    # Set our desired cash start\n",
    "    cerebro.broker.setcash(1000000.0)\n",
    "\n",
    "    # Print out the starting conditions\n",
    "    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())\n",
    "\n",
    "    # Run over everything\n",
    "    cerebro.run()\n",
    "\n",
    "    # Print out the final result\n",
    "    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Adding some Logic to the Strategy\n",
    "如果价格连续三个交易日下跌……买买买！！！\n",
    "```\n",
    "        if self.dataclose[0] < self.dataclose[-1]:\n",
    "            # current close less than previous close\n",
    "\n",
    "            if self.dataclose[-1] < self.dataclose[-2]:\n",
    "                # previous close less than the previous close\n",
    "\n",
    "                self.buy()\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Starting Portfolio Value: 100000.00\n",
      "2023-06-19, Close, 3255.81\n",
      "2023-06-20, Close, 3240.36\n",
      "2023-06-21, Close, 3197.90\n",
      "2023-06-21, BUY CREATE, 3197.90\n",
      "2023-06-26, Close, 3150.62\n",
      "2023-06-26, BUY CREATE, 3150.62\n",
      "2023-06-27, Close, 3189.44\n",
      "2023-06-28, Close, 3189.38\n",
      "2023-06-29, Close, 3182.38\n",
      "2023-06-29, BUY CREATE, 3182.38\n",
      "2023-06-30, Close, 3202.06\n",
      "2023-07-03, Close, 3243.98\n",
      "2023-07-04, Close, 3245.35\n",
      "2023-07-05, Close, 3222.95\n",
      "2023-07-06, Close, 3205.57\n",
      "2023-07-06, BUY CREATE, 3205.57\n",
      "2023-07-07, Close, 3196.61\n",
      "2023-07-07, BUY CREATE, 3196.61\n",
      "2023-07-10, Close, 3203.70\n",
      "2023-07-11, Close, 3221.37\n",
      "2023-07-12, Close, 3196.13\n",
      "2023-07-13, Close, 3236.48\n",
      "2023-07-14, Close, 3237.70\n",
      "2023-07-17, Close, 3209.63\n",
      "2023-07-18, Close, 3197.82\n",
      "2023-07-18, BUY CREATE, 3197.82\n",
      "2023-07-19, Close, 3198.84\n",
      "2023-07-20, Close, 3169.52\n",
      "2023-07-21, Close, 3167.75\n",
      "2023-07-21, BUY CREATE, 3167.75\n",
      "2023-07-24, Close, 3164.16\n",
      "2023-07-24, BUY CREATE, 3164.16\n",
      "2023-07-25, Close, 3231.52\n",
      "2023-07-26, Close, 3223.03\n",
      "2023-07-27, Close, 3216.67\n",
      "2023-07-27, BUY CREATE, 3216.67\n",
      "2023-07-28, Close, 3275.93\n",
      "2023-07-31, Close, 3291.04\n",
      "2023-08-01, Close, 3290.95\n",
      "2023-08-02, Close, 3261.69\n",
      "2023-08-02, BUY CREATE, 3261.69\n",
      "2023-08-03, Close, 3280.46\n",
      "2023-08-04, Close, 3288.08\n",
      "2023-08-07, Close, 3268.83\n",
      "2023-08-08, Close, 3260.62\n",
      "2023-08-08, BUY CREATE, 3260.62\n",
      "2023-08-09, Close, 3244.49\n",
      "2023-08-09, BUY CREATE, 3244.49\n",
      "2023-08-10, Close, 3254.56\n",
      "2023-08-11, Close, 3189.25\n",
      "2023-08-14, Close, 3178.43\n",
      "2023-08-14, BUY CREATE, 3178.43\n",
      "2023-08-15, Close, 3176.18\n",
      "2023-08-15, BUY CREATE, 3176.18\n",
      "2023-08-16, Close, 3150.13\n",
      "2023-08-16, BUY CREATE, 3150.13\n",
      "2023-08-17, Close, 3163.74\n",
      "2023-08-18, Close, 3131.95\n",
      "2023-08-21, Close, 3092.98\n",
      "2023-08-21, BUY CREATE, 3092.98\n",
      "2023-08-22, Close, 3120.33\n",
      "2023-08-23, Close, 3078.40\n",
      "2023-08-24, Close, 3082.24\n",
      "2023-08-25, Close, 3064.07\n",
      "2023-08-28, Close, 3098.64\n",
      "2023-08-29, Close, 3135.89\n",
      "2023-08-30, Close, 3137.14\n",
      "2023-08-31, Close, 3119.88\n",
      "2023-09-01, Close, 3133.25\n",
      "2023-09-04, Close, 3177.06\n",
      "2023-09-05, Close, 3154.37\n",
      "2023-09-06, Close, 3158.08\n",
      "2023-09-07, Close, 3122.35\n",
      "2023-09-08, Close, 3116.72\n",
      "2023-09-08, BUY CREATE, 3116.72\n",
      "2023-09-11, Close, 3142.78\n",
      "2023-09-12, Close, 3137.06\n",
      "2023-09-13, Close, 3123.07\n",
      "2023-09-13, BUY CREATE, 3123.07\n",
      "2023-09-14, Close, 3126.55\n",
      "2023-09-15, Close, 3117.74\n",
      "2023-09-18, Close, 3125.93\n",
      "2023-09-19, Close, 3124.96\n",
      "2023-09-20, Close, 3108.57\n",
      "2023-09-20, BUY CREATE, 3108.57\n",
      "2023-09-21, Close, 3084.70\n",
      "2023-09-21, BUY CREATE, 3084.70\n",
      "2023-09-22, Close, 3132.43\n",
      "2023-09-25, Close, 3115.61\n",
      "2023-09-26, Close, 3102.27\n",
      "2023-09-26, BUY CREATE, 3102.27\n",
      "2023-09-27, Close, 3107.32\n",
      "2023-09-28, Close, 3110.48\n",
      "2023-10-09, Close, 3096.92\n",
      "2023-10-10, Close, 3075.24\n",
      "2023-10-10, BUY CREATE, 3075.24\n",
      "2023-10-11, Close, 3078.96\n",
      "2023-10-12, Close, 3107.90\n",
      "2023-10-13, Close, 3088.10\n",
      "2023-10-16, Close, 3073.81\n",
      "2023-10-16, BUY CREATE, 3073.81\n",
      "2023-10-17, Close, 3083.50\n",
      "2023-10-18, Close, 3058.71\n",
      "2023-10-19, Close, 3005.39\n",
      "2023-10-19, BUY CREATE, 3005.39\n",
      "2023-10-20, Close, 2983.06\n",
      "2023-10-20, BUY CREATE, 2983.06\n",
      "2023-10-23, Close, 2939.29\n",
      "2023-10-23, BUY CREATE, 2939.29\n",
      "2023-10-24, Close, 2962.24\n",
      "2023-10-25, Close, 2974.11\n",
      "2023-10-26, Close, 2988.30\n",
      "2023-10-27, Close, 3017.78\n",
      "2023-10-30, Close, 3021.55\n",
      "2023-10-31, Close, 3018.77\n",
      "2023-11-01, Close, 3023.08\n",
      "2023-11-02, Close, 3009.41\n",
      "2023-11-03, Close, 3030.80\n",
      "2023-11-06, Close, 3058.41\n",
      "2023-11-07, Close, 3057.27\n",
      "2023-11-08, Close, 3052.37\n",
      "2023-11-08, BUY CREATE, 3052.37\n",
      "2023-11-09, Close, 3053.28\n",
      "2023-11-10, Close, 3038.97\n",
      "2023-11-13, Close, 3046.53\n",
      "2023-11-14, Close, 3056.07\n",
      "2023-11-15, Close, 3072.83\n",
      "2023-11-16, Close, 3050.93\n",
      "2023-11-17, Close, 3054.37\n",
      "2023-11-20, Close, 3068.32\n",
      "2023-11-21, Close, 3067.93\n",
      "2023-11-22, Close, 3043.61\n",
      "2023-11-22, BUY CREATE, 3043.61\n",
      "2023-11-23, Close, 3061.86\n",
      "2023-11-24, Close, 3040.97\n",
      "2023-11-27, Close, 3031.70\n",
      "2023-11-27, BUY CREATE, 3031.70\n",
      "2023-11-28, Close, 3038.55\n",
      "2023-11-29, Close, 3021.69\n",
      "2023-11-30, Close, 3029.67\n",
      "2023-12-01, Close, 3031.64\n",
      "2023-12-04, Close, 3022.91\n",
      "2023-12-05, Close, 2972.30\n",
      "2023-12-05, BUY CREATE, 2972.30\n",
      "2023-12-06, Close, 2968.93\n",
      "2023-12-06, BUY CREATE, 2968.93\n",
      "2023-12-07, Close, 2966.21\n",
      "2023-12-07, BUY CREATE, 2966.21\n",
      "2023-12-08, Close, 2969.56\n",
      "2023-12-11, Close, 2991.44\n",
      "2023-12-12, Close, 3003.44\n",
      "2023-12-13, Close, 2968.76\n",
      "2023-12-14, Close, 2958.99\n",
      "2023-12-14, BUY CREATE, 2958.99\n",
      "2023-12-15, Close, 2942.56\n",
      "2023-12-15, BUY CREATE, 2942.56\n",
      "2023-12-18, Close, 2930.80\n",
      "2023-12-18, BUY CREATE, 2930.80\n",
      "2023-12-19, Close, 2932.39\n",
      "2023-12-20, Close, 2902.11\n",
      "2023-12-21, Close, 2918.72\n",
      "2023-12-22, Close, 2914.77\n",
      "2023-12-25, Close, 2918.81\n",
      "2023-12-26, Close, 2898.88\n",
      "2023-12-27, Close, 2914.61\n",
      "2023-12-28, Close, 2954.70\n",
      "2023-12-29, Close, 2974.94\n",
      "2024-01-02, Close, 2962.28\n",
      "2024-01-03, Close, 2967.25\n",
      "2024-01-04, Close, 2954.35\n",
      "2024-01-05, Close, 2929.18\n",
      "2024-01-05, BUY CREATE, 2929.18\n",
      "2024-01-08, Close, 2887.54\n",
      "2024-01-08, BUY CREATE, 2887.54\n",
      "2024-01-09, Close, 2893.25\n",
      "2024-01-10, Close, 2877.70\n",
      "2024-01-11, Close, 2886.65\n",
      "2024-01-12, Close, 2881.98\n",
      "2024-01-15, Close, 2886.29\n",
      "2024-01-16, Close, 2893.99\n",
      "2024-01-17, Close, 2833.61\n",
      "2024-01-18, Close, 2845.78\n",
      "2024-01-19, Close, 2832.28\n",
      "2024-01-22, Close, 2756.34\n",
      "2024-01-22, BUY CREATE, 2756.34\n",
      "2024-01-23, Close, 2770.98\n",
      "2024-01-24, Close, 2820.77\n",
      "2024-01-25, Close, 2906.11\n",
      "2024-01-26, Close, 2910.22\n",
      "2024-01-29, Close, 2883.36\n",
      "2024-01-30, Close, 2830.53\n",
      "2024-01-30, BUY CREATE, 2830.53\n",
      "2024-01-31, Close, 2788.55\n",
      "2024-01-31, BUY CREATE, 2788.55\n",
      "2024-02-01, Close, 2770.74\n",
      "2024-02-01, BUY CREATE, 2770.74\n",
      "2024-02-02, Close, 2730.15\n",
      "2024-02-02, BUY CREATE, 2730.15\n",
      "2024-02-05, Close, 2702.19\n",
      "2024-02-05, BUY CREATE, 2702.19\n",
      "2024-02-06, Close, 2789.49\n",
      "2024-02-07, Close, 2829.70\n",
      "2024-02-08, Close, 2865.90\n",
      "2024-02-19, Close, 2910.54\n",
      "2024-02-20, Close, 2922.73\n",
      "2024-02-21, Close, 2950.96\n",
      "2024-02-22, Close, 2988.36\n",
      "2024-02-23, Close, 3004.88\n",
      "2024-02-26, Close, 2977.02\n",
      "2024-02-27, Close, 3015.48\n",
      "2024-02-28, Close, 2957.85\n",
      "2024-02-29, Close, 3015.17\n",
      "2024-03-01, Close, 3027.02\n",
      "2024-03-04, Close, 3039.31\n",
      "2024-03-05, Close, 3047.79\n",
      "2024-03-06, Close, 3039.93\n",
      "2024-03-07, Close, 3027.40\n",
      "2024-03-07, BUY CREATE, 3027.40\n",
      "2024-03-08, Close, 3046.02\n",
      "2024-03-11, Close, 3068.46\n",
      "2024-03-12, Close, 3055.94\n",
      "2024-03-13, Close, 3043.83\n",
      "2024-03-13, BUY CREATE, 3043.83\n",
      "2024-03-14, Close, 3038.23\n",
      "2024-03-14, BUY CREATE, 3038.23\n",
      "2024-03-15, Close, 3054.64\n",
      "2024-03-18, Close, 3084.93\n",
      "2024-03-19, Close, 3062.76\n",
      "2024-03-20, Close, 3079.69\n",
      "2024-03-21, Close, 3077.11\n",
      "2024-03-22, Close, 3048.03\n",
      "2024-03-22, BUY CREATE, 3048.03\n",
      "2024-03-25, Close, 3026.31\n",
      "2024-03-25, BUY CREATE, 3026.31\n",
      "2024-03-26, Close, 3031.48\n",
      "2024-03-27, Close, 2993.14\n",
      "2024-03-28, Close, 3010.66\n",
      "2024-03-29, Close, 3041.17\n",
      "2024-04-01, Close, 3077.38\n",
      "2024-04-02, Close, 3074.96\n",
      "2024-04-03, Close, 3069.30\n",
      "2024-04-03, BUY CREATE, 3069.30\n",
      "2024-04-08, Close, 3047.05\n",
      "2024-04-08, BUY CREATE, 3047.05\n",
      "2024-04-09, Close, 3048.54\n",
      "2024-04-10, Close, 3027.33\n",
      "2024-04-11, Close, 3034.25\n",
      "2024-04-12, Close, 3019.47\n",
      "2024-04-15, Close, 3057.38\n",
      "2024-04-16, Close, 3007.07\n",
      "2024-04-17, Close, 3071.38\n",
      "2024-04-18, Close, 3074.23\n",
      "2024-04-19, Close, 3065.26\n",
      "2024-04-22, Close, 3044.59\n",
      "2024-04-22, BUY CREATE, 3044.59\n",
      "2024-04-23, Close, 3021.98\n",
      "2024-04-23, BUY CREATE, 3021.98\n",
      "2024-04-24, Close, 3044.82\n",
      "2024-04-25, Close, 3052.90\n",
      "2024-04-26, Close, 3088.64\n",
      "2024-04-29, Close, 3113.04\n",
      "2024-04-30, Close, 3104.82\n",
      "2024-05-06, Close, 3140.72\n",
      "2024-05-07, Close, 3147.74\n",
      "2024-05-08, Close, 3128.48\n",
      "2024-05-09, Close, 3154.32\n",
      "2024-05-10, Close, 3154.55\n",
      "2024-05-13, Close, 3148.02\n",
      "2024-05-14, Close, 3145.77\n",
      "2024-05-14, BUY CREATE, 3145.77\n",
      "2024-05-15, Close, 3119.90\n",
      "2024-05-15, BUY CREATE, 3119.90\n",
      "2024-05-16, Close, 3122.40\n",
      "2024-05-17, Close, 3154.03\n",
      "2024-05-20, Close, 3171.15\n",
      "2024-05-21, Close, 3157.97\n",
      "2024-05-22, Close, 3158.54\n",
      "2024-05-23, Close, 3116.39\n",
      "2024-05-24, Close, 3088.87\n",
      "2024-05-24, BUY CREATE, 3088.87\n",
      "2024-05-27, Close, 3124.04\n",
      "2024-05-28, Close, 3109.57\n",
      "2024-05-29, Close, 3111.02\n",
      "2024-05-30, Close, 3091.68\n",
      "2024-05-31, Close, 3086.81\n",
      "2024-05-31, BUY CREATE, 3086.81\n",
      "2024-06-03, Close, 3078.49\n",
      "2024-06-03, BUY CREATE, 3078.49\n",
      "2024-06-04, Close, 3091.20\n",
      "2024-06-05, Close, 3065.40\n",
      "2024-06-06, Close, 3048.79\n",
      "2024-06-06, BUY CREATE, 3048.79\n",
      "2024-06-07, Close, 3051.28\n",
      "2024-06-11, Close, 3028.04\n",
      "2024-06-12, Close, 3037.47\n",
      "2024-06-13, Close, 3028.92\n",
      "2024-06-14, Close, 3032.63\n",
      "2024-06-17, Close, 3015.89\n",
      "Final Portfolio Value: 96873.88\n"
     ]
    }
   ],
   "source": [
    "from __future__ import (absolute_import, division, print_function,\n",
    "                        unicode_literals)\n",
    "\n",
    "import datetime  # For datetime objects\n",
    "import os.path  # To manage paths\n",
    "import sys  # To find out the script name (in argv[0])\n",
    "\n",
    "# Import the backtrader platform\n",
    "import backtrader as bt\n",
    "\n",
    "\n",
    "# ====================================Create a Stratey=====================================\n",
    "class TestStrategy(bt.Strategy):\n",
    "\n",
    "    def log(self, txt, dt=None):\n",
    "        ''' Logging function fot this strategy'''\n",
    "        dt = dt or self.datas[0].datetime.date(0)\n",
    "        print('%s, %s' % (dt.isoformat(), txt))\n",
    "\n",
    "    def __init__(self):\n",
    "        # Keep a reference to the \"close\" line in the data[0] dataseries\n",
    "        self.dataclose = self.datas[0].close\n",
    "\n",
    "    def next(self):\n",
    "        # Simply log the closing price of the series from the reference\n",
    "        self.log('Close, %.2f' % self.dataclose[0])\n",
    "\n",
    "        if self.dataclose[0] < self.dataclose[-1]:\n",
    "            # current close less than previous close\n",
    "\n",
    "            if self.dataclose[-1] < self.dataclose[-2]:\n",
    "                # previous close less than the previous close\n",
    "\n",
    "                # BUY, BUY, BUY!!! (with all possible default parameters)\n",
    "                self.log('BUY CREATE, %.2f' % self.dataclose[0])\n",
    "                self.buy()\n",
    "# =======================================================================================\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    # Create a cerebro entity\n",
    "    cerebro = bt.Cerebro()\n",
    "\n",
    "    # Add a strategy\n",
    "    cerebro.addstrategy(TestStrategy)\n",
    "\n",
    "    # Datas are in a subfolder of the samples. Need to find where the script is\n",
    "    # because it could have been called from anywhere\n",
    "    # modpath = os.path.dirname(os.path.abspath(sys.argv[0]))\n",
    "    modpath = 'C:\\\\Users\\\\bww\\gitee\\\\stock\\\\backtrader'\n",
    "    datapath = os.path.join(modpath, '000001.SS.csv')\n",
    "\n",
    "    # Create a Data Feed\n",
    "    data = bt.feeds.YahooFinanceCSVData(\n",
    "        dataname=datapath,\n",
    "        # Do not pass values before this date\n",
    "        fromdate=datetime.datetime(2000, 1, 1),\n",
    "        # Do not pass values before this date\n",
    "        todate=datetime.datetime(2024, 12, 31),\n",
    "        # Do not pass values after this date\n",
    "        reverse=False)\n",
    "\n",
    "    # Add the Data Feed to Cerebro\n",
    "    cerebro.adddata(data)\n",
    "\n",
    "    # Set our desired cash start\n",
    "    cerebro.broker.setcash(100000.0)\n",
    "\n",
    "    # Print out the starting conditions\n",
    "    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())\n",
    "\n",
    "    # Run over everything\n",
    "    cerebro.run()\n",
    "\n",
    "    # Print out the final result\n",
    "    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Do not only buy … but SELL\n",
    "无论好坏，在 5 个柱（在第 6 个 th 柱上）过去后退出"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Starting Portfolio Value: 100000.00\n",
      "2023-06-19, Close, 3255.81\n",
      "2023-06-20, Close, 3240.36\n",
      "2023-06-21, Close, 3197.90\n",
      "2023-06-21, BUY CREATE, 3197.90\n",
      "2023-06-26, BUY EXECUTED, 3177.23\n",
      "2023-06-26, Close, 3150.62\n",
      "2023-06-27, Close, 3189.44\n",
      "2023-06-28, Close, 3189.38\n",
      "2023-06-29, Close, 3182.38\n",
      "2023-06-30, Close, 3202.06\n",
      "2023-07-03, Close, 3243.98\n",
      "2023-07-03, SELL CREATE, 3243.98\n",
      "2023-07-04, SELL EXECUTED, 3241.23\n",
      "2023-07-04, Close, 3245.35\n",
      "2023-07-05, Close, 3222.95\n",
      "2023-07-06, Close, 3205.57\n",
      "2023-07-06, BUY CREATE, 3205.57\n",
      "2023-07-07, BUY EXECUTED, 3197.47\n",
      "2023-07-07, Close, 3196.61\n",
      "2023-07-10, Close, 3203.70\n",
      "2023-07-11, Close, 3221.37\n",
      "2023-07-12, Close, 3196.13\n",
      "2023-07-13, Close, 3236.48\n",
      "2023-07-14, Close, 3237.70\n",
      "2023-07-14, SELL CREATE, 3237.70\n",
      "2023-07-17, SELL EXECUTED, 3219.02\n",
      "2023-07-17, Close, 3209.63\n",
      "2023-07-18, Close, 3197.82\n",
      "2023-07-18, BUY CREATE, 3197.82\n",
      "2023-07-19, BUY EXECUTED, 3195.01\n",
      "2023-07-19, Close, 3198.84\n",
      "2023-07-20, Close, 3169.52\n",
      "2023-07-21, Close, 3167.75\n",
      "2023-07-24, Close, 3164.16\n",
      "2023-07-25, Close, 3231.52\n",
      "2023-07-26, Close, 3223.03\n",
      "2023-07-26, SELL CREATE, 3223.03\n",
      "2023-07-27, SELL EXECUTED, 3225.48\n",
      "2023-07-27, Close, 3216.67\n",
      "2023-07-27, BUY CREATE, 3216.67\n",
      "2023-07-28, BUY EXECUTED, 3206.74\n",
      "2023-07-28, Close, 3275.93\n",
      "2023-07-31, Close, 3291.04\n",
      "2023-08-01, Close, 3290.95\n",
      "2023-08-02, Close, 3261.69\n",
      "2023-08-03, Close, 3280.46\n",
      "2023-08-04, Close, 3288.08\n",
      "2023-08-04, SELL CREATE, 3288.08\n",
      "2023-08-07, SELL EXECUTED, 3276.78\n",
      "2023-08-07, Close, 3268.83\n",
      "2023-08-08, Close, 3260.62\n",
      "2023-08-08, BUY CREATE, 3260.62\n",
      "2023-08-09, BUY EXECUTED, 3251.15\n",
      "2023-08-09, Close, 3244.49\n",
      "2023-08-10, Close, 3254.56\n",
      "2023-08-11, Close, 3189.25\n",
      "2023-08-14, Close, 3178.43\n",
      "2023-08-15, Close, 3176.18\n",
      "2023-08-16, Close, 3150.13\n",
      "2023-08-16, SELL CREATE, 3150.13\n",
      "2023-08-17, SELL EXECUTED, 3137.35\n",
      "2023-08-17, Close, 3163.74\n",
      "2023-08-18, Close, 3131.95\n",
      "2023-08-21, Close, 3092.98\n",
      "2023-08-21, BUY CREATE, 3092.98\n",
      "2023-08-22, BUY EXECUTED, 3103.78\n",
      "2023-08-22, Close, 3120.33\n",
      "2023-08-23, Close, 3078.40\n",
      "2023-08-24, Close, 3082.24\n",
      "2023-08-25, Close, 3064.07\n",
      "2023-08-28, Close, 3098.64\n",
      "2023-08-29, Close, 3135.89\n",
      "2023-08-29, SELL CREATE, 3135.89\n",
      "2023-08-30, SELL EXECUTED, 3144.41\n",
      "2023-08-30, Close, 3137.14\n",
      "2023-08-31, Close, 3119.88\n",
      "2023-09-01, Close, 3133.25\n",
      "2023-09-04, Close, 3177.06\n",
      "2023-09-05, Close, 3154.37\n",
      "2023-09-06, Close, 3158.08\n",
      "2023-09-07, Close, 3122.35\n",
      "2023-09-08, Close, 3116.72\n",
      "2023-09-08, BUY CREATE, 3116.72\n",
      "2023-09-11, BUY EXECUTED, 3119.61\n",
      "2023-09-11, Close, 3142.78\n",
      "2023-09-12, Close, 3137.06\n",
      "2023-09-13, Close, 3123.07\n",
      "2023-09-14, Close, 3126.55\n",
      "2023-09-15, Close, 3117.74\n",
      "2023-09-18, Close, 3125.93\n",
      "2023-09-18, SELL CREATE, 3125.93\n",
      "2023-09-19, SELL EXECUTED, 3123.99\n",
      "2023-09-19, Close, 3124.96\n",
      "2023-09-20, Close, 3108.57\n",
      "2023-09-20, BUY CREATE, 3108.57\n",
      "2023-09-21, BUY EXECUTED, 3103.18\n",
      "2023-09-21, Close, 3084.70\n",
      "2023-09-22, Close, 3132.43\n",
      "2023-09-25, Close, 3115.61\n",
      "2023-09-26, Close, 3102.27\n",
      "2023-09-27, Close, 3107.32\n",
      "2023-09-28, Close, 3110.48\n",
      "2023-09-28, SELL CREATE, 3110.48\n",
      "2023-10-09, SELL EXECUTED, 3100.00\n",
      "2023-10-09, Close, 3096.92\n",
      "2023-10-10, Close, 3075.24\n",
      "2023-10-10, BUY CREATE, 3075.24\n",
      "2023-10-11, BUY EXECUTED, 3085.70\n",
      "2023-10-11, Close, 3078.96\n",
      "2023-10-12, Close, 3107.90\n",
      "2023-10-13, Close, 3088.10\n",
      "2023-10-16, Close, 3073.81\n",
      "2023-10-17, Close, 3083.50\n",
      "2023-10-18, Close, 3058.71\n",
      "2023-10-18, SELL CREATE, 3058.71\n",
      "2023-10-19, SELL EXECUTED, 3043.20\n",
      "2023-10-19, Close, 3005.39\n",
      "2023-10-19, BUY CREATE, 3005.39\n",
      "2023-10-20, BUY EXECUTED, 2995.36\n",
      "2023-10-20, Close, 2983.06\n",
      "2023-10-23, Close, 2939.29\n",
      "2023-10-24, Close, 2962.24\n",
      "2023-10-25, Close, 2974.11\n",
      "2023-10-26, Close, 2988.30\n",
      "2023-10-27, Close, 3017.78\n",
      "2023-10-27, SELL CREATE, 3017.78\n",
      "2023-10-30, SELL EXECUTED, 3009.61\n",
      "2023-10-30, Close, 3021.55\n",
      "2023-10-31, Close, 3018.77\n",
      "2023-11-01, Close, 3023.08\n",
      "2023-11-02, Close, 3009.41\n",
      "2023-11-03, Close, 3030.80\n",
      "2023-11-06, Close, 3058.41\n",
      "2023-11-07, Close, 3057.27\n",
      "2023-11-08, Close, 3052.37\n",
      "2023-11-08, BUY CREATE, 3052.37\n",
      "2023-11-09, BUY EXECUTED, 3047.65\n",
      "2023-11-09, Close, 3053.28\n",
      "2023-11-10, Close, 3038.97\n",
      "2023-11-13, Close, 3046.53\n",
      "2023-11-14, Close, 3056.07\n",
      "2023-11-15, Close, 3072.83\n",
      "2023-11-16, Close, 3050.93\n",
      "2023-11-16, SELL CREATE, 3050.93\n",
      "2023-11-17, SELL EXECUTED, 3043.62\n",
      "2023-11-17, Close, 3054.37\n",
      "2023-11-20, Close, 3068.32\n",
      "2023-11-21, Close, 3067.93\n",
      "2023-11-22, Close, 3043.61\n",
      "2023-11-22, BUY CREATE, 3043.61\n",
      "2023-11-23, BUY EXECUTED, 3041.68\n",
      "2023-11-23, Close, 3061.86\n",
      "2023-11-24, Close, 3040.97\n",
      "2023-11-27, Close, 3031.70\n",
      "2023-11-28, Close, 3038.55\n",
      "2023-11-29, Close, 3021.69\n",
      "2023-11-30, Close, 3029.67\n",
      "2023-11-30, SELL CREATE, 3029.67\n",
      "2023-12-01, SELL EXECUTED, 3027.35\n",
      "2023-12-01, Close, 3031.64\n",
      "2023-12-04, Close, 3022.91\n",
      "2023-12-05, Close, 2972.30\n",
      "2023-12-05, BUY CREATE, 2972.30\n",
      "2023-12-06, BUY EXECUTED, 2966.95\n",
      "2023-12-06, Close, 2968.93\n",
      "2023-12-07, Close, 2966.21\n",
      "2023-12-08, Close, 2969.56\n",
      "2023-12-11, Close, 2991.44\n",
      "2023-12-12, Close, 3003.44\n",
      "2023-12-13, Close, 2968.76\n",
      "2023-12-13, SELL CREATE, 2968.76\n",
      "2023-12-14, SELL EXECUTED, 2979.70\n",
      "2023-12-14, Close, 2958.99\n",
      "2023-12-14, BUY CREATE, 2958.99\n",
      "2023-12-15, BUY EXECUTED, 2966.30\n",
      "2023-12-15, Close, 2942.56\n",
      "2023-12-18, Close, 2930.80\n",
      "2023-12-19, Close, 2932.39\n",
      "2023-12-20, Close, 2902.11\n",
      "2023-12-21, Close, 2918.72\n",
      "2023-12-22, Close, 2914.77\n",
      "2023-12-22, SELL CREATE, 2914.77\n",
      "2023-12-25, SELL EXECUTED, 2909.88\n",
      "2023-12-25, Close, 2918.81\n",
      "2023-12-26, Close, 2898.88\n",
      "2023-12-27, Close, 2914.61\n",
      "2023-12-28, Close, 2954.70\n",
      "2023-12-29, Close, 2974.94\n",
      "2024-01-02, Close, 2962.28\n",
      "2024-01-03, Close, 2967.25\n",
      "2024-01-04, Close, 2954.35\n",
      "2024-01-05, Close, 2929.18\n",
      "2024-01-05, BUY CREATE, 2929.18\n",
      "2024-01-08, BUY EXECUTED, 2923.46\n",
      "2024-01-08, Close, 2887.54\n",
      "2024-01-09, Close, 2893.25\n",
      "2024-01-10, Close, 2877.70\n",
      "2024-01-11, Close, 2886.65\n",
      "2024-01-12, Close, 2881.98\n",
      "2024-01-15, Close, 2886.29\n",
      "2024-01-15, SELL CREATE, 2886.29\n",
      "2024-01-16, SELL EXECUTED, 2881.56\n",
      "2024-01-16, Close, 2893.99\n",
      "2024-01-17, Close, 2833.61\n",
      "2024-01-18, Close, 2845.78\n",
      "2024-01-19, Close, 2832.28\n",
      "2024-01-22, Close, 2756.34\n",
      "2024-01-22, BUY CREATE, 2756.34\n",
      "2024-01-23, BUY EXECUTED, 2746.89\n",
      "2024-01-23, Close, 2770.98\n",
      "2024-01-24, Close, 2820.77\n",
      "2024-01-25, Close, 2906.11\n",
      "2024-01-26, Close, 2910.22\n",
      "2024-01-29, Close, 2883.36\n",
      "2024-01-30, Close, 2830.53\n",
      "2024-01-30, SELL CREATE, 2830.53\n",
      "2024-01-31, SELL EXECUTED, 2815.50\n",
      "2024-01-31, Close, 2788.55\n",
      "2024-01-31, BUY CREATE, 2788.55\n",
      "2024-02-01, BUY EXECUTED, 2773.42\n",
      "2024-02-01, Close, 2770.74\n",
      "2024-02-02, Close, 2730.15\n",
      "2024-02-05, Close, 2702.19\n",
      "2024-02-06, Close, 2789.49\n",
      "2024-02-07, Close, 2829.70\n",
      "2024-02-08, Close, 2865.90\n",
      "2024-02-08, SELL CREATE, 2865.90\n",
      "2024-02-19, SELL EXECUTED, 2886.59\n",
      "2024-02-19, Close, 2910.54\n",
      "2024-02-20, Close, 2922.73\n",
      "2024-02-21, Close, 2950.96\n",
      "2024-02-22, Close, 2988.36\n",
      "2024-02-23, Close, 3004.88\n",
      "2024-02-26, Close, 2977.02\n",
      "2024-02-27, Close, 3015.48\n",
      "2024-02-28, Close, 2957.85\n",
      "2024-02-29, Close, 3015.17\n",
      "2024-03-01, Close, 3027.02\n",
      "2024-03-04, Close, 3039.31\n",
      "2024-03-05, Close, 3047.79\n",
      "2024-03-06, Close, 3039.93\n",
      "2024-03-07, Close, 3027.40\n",
      "2024-03-07, BUY CREATE, 3027.40\n",
      "2024-03-08, BUY EXECUTED, 3026.17\n",
      "2024-03-08, Close, 3046.02\n",
      "2024-03-11, Close, 3068.46\n",
      "2024-03-12, Close, 3055.94\n",
      "2024-03-13, Close, 3043.83\n",
      "2024-03-14, Close, 3038.23\n",
      "2024-03-15, Close, 3054.64\n",
      "2024-03-15, SELL CREATE, 3054.64\n",
      "2024-03-18, SELL EXECUTED, 3057.21\n",
      "2024-03-18, Close, 3084.93\n",
      "2024-03-19, Close, 3062.76\n",
      "2024-03-20, Close, 3079.69\n",
      "2024-03-21, Close, 3077.11\n",
      "2024-03-22, Close, 3048.03\n",
      "2024-03-22, BUY CREATE, 3048.03\n",
      "2024-03-25, BUY EXECUTED, 3040.50\n",
      "2024-03-25, Close, 3026.31\n",
      "2024-03-26, Close, 3031.48\n",
      "2024-03-27, Close, 2993.14\n",
      "2024-03-28, Close, 3010.66\n",
      "2024-03-29, Close, 3041.17\n",
      "2024-04-01, Close, 3077.38\n",
      "2024-04-01, SELL CREATE, 3077.38\n",
      "2024-04-02, SELL EXECUTED, 3076.88\n",
      "2024-04-02, Close, 3074.96\n",
      "2024-04-03, Close, 3069.30\n",
      "2024-04-03, BUY CREATE, 3069.30\n",
      "2024-04-08, BUY EXECUTED, 3057.33\n",
      "2024-04-08, Close, 3047.05\n",
      "2024-04-09, Close, 3048.54\n",
      "2024-04-10, Close, 3027.33\n",
      "2024-04-11, Close, 3034.25\n",
      "2024-04-12, Close, 3019.47\n",
      "2024-04-15, Close, 3057.38\n",
      "2024-04-15, SELL CREATE, 3057.38\n",
      "2024-04-16, SELL EXECUTED, 3043.55\n",
      "2024-04-16, Close, 3007.07\n",
      "2024-04-17, Close, 3071.38\n",
      "2024-04-18, Close, 3074.23\n",
      "2024-04-19, Close, 3065.26\n",
      "2024-04-22, Close, 3044.59\n",
      "2024-04-22, BUY CREATE, 3044.59\n",
      "2024-04-23, BUY EXECUTED, 3038.61\n",
      "2024-04-23, Close, 3021.98\n",
      "2024-04-24, Close, 3044.82\n",
      "2024-04-25, Close, 3052.90\n",
      "2024-04-26, Close, 3088.64\n",
      "2024-04-29, Close, 3113.04\n",
      "2024-04-30, Close, 3104.82\n",
      "2024-04-30, SELL CREATE, 3104.82\n",
      "2024-05-06, SELL EXECUTED, 3132.51\n",
      "2024-05-06, Close, 3140.72\n",
      "2024-05-07, Close, 3147.74\n",
      "2024-05-08, Close, 3128.48\n",
      "2024-05-09, Close, 3154.32\n",
      "2024-05-10, Close, 3154.55\n",
      "2024-05-13, Close, 3148.02\n",
      "2024-05-14, Close, 3145.77\n",
      "2024-05-14, BUY CREATE, 3145.77\n",
      "2024-05-15, BUY EXECUTED, 3141.25\n",
      "2024-05-15, Close, 3119.90\n",
      "2024-05-16, Close, 3122.40\n",
      "2024-05-17, Close, 3154.03\n",
      "2024-05-20, Close, 3171.15\n",
      "2024-05-21, Close, 3157.97\n",
      "2024-05-22, Close, 3158.54\n",
      "2024-05-22, SELL CREATE, 3158.54\n",
      "2024-05-23, SELL EXECUTED, 3149.88\n",
      "2024-05-23, Close, 3116.39\n",
      "2024-05-24, Close, 3088.87\n",
      "2024-05-24, BUY CREATE, 3088.87\n",
      "2024-05-27, BUY EXECUTED, 3094.14\n",
      "2024-05-27, Close, 3124.04\n",
      "2024-05-28, Close, 3109.57\n",
      "2024-05-29, Close, 3111.02\n",
      "2024-05-30, Close, 3091.68\n",
      "2024-05-31, Close, 3086.81\n",
      "2024-06-03, Close, 3078.49\n",
      "2024-06-03, SELL CREATE, 3078.49\n",
      "2024-06-04, SELL EXECUTED, 3071.32\n",
      "2024-06-04, Close, 3091.20\n",
      "2024-06-05, Close, 3065.40\n",
      "2024-06-06, Close, 3048.79\n",
      "2024-06-06, BUY CREATE, 3048.79\n",
      "2024-06-07, BUY EXECUTED, 3053.92\n",
      "2024-06-07, Close, 3051.28\n",
      "2024-06-11, Close, 3028.04\n",
      "2024-06-12, Close, 3037.47\n",
      "2024-06-13, Close, 3028.92\n",
      "2024-06-14, Close, 3032.63\n",
      "2024-06-17, Close, 3015.89\n",
      "2024-06-17, SELL CREATE, 3015.89\n",
      "Final Portfolio Value: 100259.01\n"
     ]
    },
    {
     "data": {
      "application/javascript": "/* Put everything inside the global mpl namespace */\n/* global mpl */\nwindow.mpl = {};\n\nmpl.get_websocket_type = function () {\n    if (typeof WebSocket !== 'undefined') {\n        return WebSocket;\n    } else if (typeof MozWebSocket !== 'undefined') {\n        return MozWebSocket;\n    } else {\n        alert(\n            'Your browser does not have WebSocket support. ' +\n                'Please try Chrome, Safari or Firefox ≥ 6. ' +\n                'Firefox 4 and 5 are also supported but you ' +\n                'have to enable WebSockets in about:config.'\n        );\n    }\n};\n\nmpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n    this.id = figure_id;\n\n    this.ws = websocket;\n\n    this.supports_binary = this.ws.binaryType !== undefined;\n\n    if (!this.supports_binary) {\n        var warnings = document.getElementById('mpl-warnings');\n        if (warnings) {\n            warnings.style.display = 'block';\n            warnings.textContent =\n                'This browser does not support binary websocket messages. ' +\n                'Performance may be slow.';\n        }\n    }\n\n    this.imageObj = new Image();\n\n    this.context = undefined;\n    this.message = undefined;\n    this.canvas = undefined;\n    this.rubberband_canvas = undefined;\n    this.rubberband_context = undefined;\n    this.format_dropdown = undefined;\n\n    this.image_mode = 'full';\n\n    this.root = document.createElement('div');\n    this.root.setAttribute('style', 'display: inline-block');\n    this._root_extra_style(this.root);\n\n    parent_element.appendChild(this.root);\n\n    this._init_header(this);\n    this._init_canvas(this);\n    this._init_toolbar(this);\n\n    var fig = this;\n\n    this.waiting = false;\n\n    this.ws.onopen = function () {\n        fig.send_message('supports_binary', { value: fig.supports_binary });\n        fig.send_message('send_image_mode', {});\n        if (fig.ratio !== 1) {\n            fig.send_message('set_device_pixel_ratio', {\n                device_pixel_ratio: fig.ratio,\n            });\n        }\n        fig.send_message('refresh', {});\n    };\n\n    this.imageObj.onload = function () {\n        if (fig.image_mode === 'full') {\n            // Full images could contain transparency (where diff images\n            // almost always do), so we need to clear the canvas so that\n            // there is no ghosting.\n            fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n        }\n        fig.context.drawImage(fig.imageObj, 0, 0);\n    };\n\n    this.imageObj.onunload = function () {\n        fig.ws.close();\n    };\n\n    this.ws.onmessage = this._make_on_message_function(this);\n\n    this.ondownload = ondownload;\n};\n\nmpl.figure.prototype._init_header = function () {\n    var titlebar = document.createElement('div');\n    titlebar.classList =\n        'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n    var titletext = document.createElement('div');\n    titletext.classList = 'ui-dialog-title';\n    titletext.setAttribute(\n        'style',\n        'width: 100%; text-align: center; padding: 3px;'\n    );\n    titlebar.appendChild(titletext);\n    this.root.appendChild(titlebar);\n    this.header = titletext;\n};\n\nmpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._init_canvas = function () {\n    var fig = this;\n\n    var canvas_div = (this.canvas_div = document.createElement('div'));\n    canvas_div.setAttribute('tabindex', '0');\n    canvas_div.setAttribute(\n        'style',\n        'border: 1px solid #ddd;' +\n            'box-sizing: content-box;' +\n            'clear: both;' +\n            'min-height: 1px;' +\n            'min-width: 1px;' +\n            'outline: 0;' +\n            'overflow: hidden;' +\n            'position: relative;' +\n            'resize: both;' +\n            'z-index: 2;'\n    );\n\n    function on_keyboard_event_closure(name) {\n        return function (event) {\n            return fig.key_event(event, name);\n        };\n    }\n\n    canvas_div.addEventListener(\n        'keydown',\n        on_keyboard_event_closure('key_press')\n    );\n    canvas_div.addEventListener(\n        'keyup',\n        on_keyboard_event_closure('key_release')\n    );\n\n    this._canvas_extra_style(canvas_div);\n    this.root.appendChild(canvas_div);\n\n    var canvas = (this.canvas = document.createElement('canvas'));\n    canvas.classList.add('mpl-canvas');\n    canvas.setAttribute(\n        'style',\n        'box-sizing: content-box;' +\n            'pointer-events: none;' +\n            'position: relative;' +\n            'z-index: 0;'\n    );\n\n    this.context = canvas.getContext('2d');\n\n    var backingStore =\n        this.context.backingStorePixelRatio ||\n        this.context.webkitBackingStorePixelRatio ||\n        this.context.mozBackingStorePixelRatio ||\n        this.context.msBackingStorePixelRatio ||\n        this.context.oBackingStorePixelRatio ||\n        this.context.backingStorePixelRatio ||\n        1;\n\n    this.ratio = (window.devicePixelRatio || 1) / backingStore;\n\n    var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n        'canvas'\n    ));\n    rubberband_canvas.setAttribute(\n        'style',\n        'box-sizing: content-box;' +\n            'left: 0;' +\n            'pointer-events: none;' +\n            'position: absolute;' +\n            'top: 0;' +\n            'z-index: 1;'\n    );\n\n    // Apply a ponyfill if ResizeObserver is not implemented by browser.\n    if (this.ResizeObserver === undefined) {\n        if (window.ResizeObserver !== undefined) {\n            this.ResizeObserver = window.ResizeObserver;\n        } else {\n            var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n            this.ResizeObserver = obs.ResizeObserver;\n        }\n    }\n\n    this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n        var nentries = entries.length;\n        for (var i = 0; i < nentries; i++) {\n            var entry = entries[i];\n            var width, height;\n            if (entry.contentBoxSize) {\n                if (entry.contentBoxSize instanceof Array) {\n                    // Chrome 84 implements new version of spec.\n                    width = entry.contentBoxSize[0].inlineSize;\n                    height = entry.contentBoxSize[0].blockSize;\n                } else {\n                    // Firefox implements old version of spec.\n                    width = entry.contentBoxSize.inlineSize;\n                    height = entry.contentBoxSize.blockSize;\n                }\n            } else {\n                // Chrome <84 implements even older version of spec.\n                width = entry.contentRect.width;\n                height = entry.contentRect.height;\n            }\n\n            // Keep the size of the canvas and rubber band canvas in sync with\n            // the canvas container.\n            if (entry.devicePixelContentBoxSize) {\n                // Chrome 84 implements new version of spec.\n                canvas.setAttribute(\n                    'width',\n                    entry.devicePixelContentBoxSize[0].inlineSize\n                );\n                canvas.setAttribute(\n                    'height',\n                    entry.devicePixelContentBoxSize[0].blockSize\n                );\n            } else {\n                canvas.setAttribute('width', width * fig.ratio);\n                canvas.setAttribute('height', height * fig.ratio);\n            }\n            /* This rescales the canvas back to display pixels, so that it\n             * appears correct on HiDPI screens. */\n            canvas.style.width = width + 'px';\n            canvas.style.height = height + 'px';\n\n            rubberband_canvas.setAttribute('width', width);\n            rubberband_canvas.setAttribute('height', height);\n\n            // And update the size in Python. We ignore the initial 0/0 size\n            // that occurs as the element is placed into the DOM, which should\n            // otherwise not happen due to the minimum size styling.\n            if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n                fig.request_resize(width, height);\n            }\n        }\n    });\n    this.resizeObserverInstance.observe(canvas_div);\n\n    function on_mouse_event_closure(name) {\n        /* User Agent sniffing is bad, but WebKit is busted:\n         * https://bugs.webkit.org/show_bug.cgi?id=144526\n         * https://bugs.webkit.org/show_bug.cgi?id=181818\n         * The worst that happens here is that they get an extra browser\n         * selection when dragging, if this check fails to catch them.\n         */\n        var UA = navigator.userAgent;\n        var isWebKit = /AppleWebKit/.test(UA) && !/Chrome/.test(UA);\n        if(isWebKit) {\n            return function (event) {\n                /* This prevents the web browser from automatically changing to\n                 * the text insertion cursor when the button is pressed. We\n                 * want to control all of the cursor setting manually through\n                 * the 'cursor' event from matplotlib */\n                event.preventDefault()\n                return fig.mouse_event(event, name);\n            };\n        } else {\n            return function (event) {\n                return fig.mouse_event(event, name);\n            };\n        }\n    }\n\n    canvas_div.addEventListener(\n        'mousedown',\n        on_mouse_event_closure('button_press')\n    );\n    canvas_div.addEventListener(\n        'mouseup',\n        on_mouse_event_closure('button_release')\n    );\n    canvas_div.addEventListener(\n        'dblclick',\n        on_mouse_event_closure('dblclick')\n    );\n    // Throttle sequential mouse events to 1 every 20ms.\n    canvas_div.addEventListener(\n        'mousemove',\n        on_mouse_event_closure('motion_notify')\n    );\n\n    canvas_div.addEventListener(\n        'mouseenter',\n        on_mouse_event_closure('figure_enter')\n    );\n    canvas_div.addEventListener(\n        'mouseleave',\n        on_mouse_event_closure('figure_leave')\n    );\n\n    canvas_div.addEventListener('wheel', function (event) {\n        if (event.deltaY < 0) {\n            event.step = 1;\n        } else {\n            event.step = -1;\n        }\n        on_mouse_event_closure('scroll')(event);\n    });\n\n    canvas_div.appendChild(canvas);\n    canvas_div.appendChild(rubberband_canvas);\n\n    this.rubberband_context = rubberband_canvas.getContext('2d');\n    this.rubberband_context.strokeStyle = '#000000';\n\n    this._resize_canvas = function (width, height, forward) {\n        if (forward) {\n            canvas_div.style.width = width + 'px';\n            canvas_div.style.height = height + 'px';\n        }\n    };\n\n    // Disable right mouse context menu.\n    canvas_div.addEventListener('contextmenu', function (_e) {\n        event.preventDefault();\n        return false;\n    });\n\n    function set_focus() {\n        canvas.focus();\n        canvas_div.focus();\n    }\n\n    window.setTimeout(set_focus, 100);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n    var fig = this;\n\n    var toolbar = document.createElement('div');\n    toolbar.classList = 'mpl-toolbar';\n    this.root.appendChild(toolbar);\n\n    function on_click_closure(name) {\n        return function (_event) {\n            return fig.toolbar_button_onclick(name);\n        };\n    }\n\n    function on_mouseover_closure(tooltip) {\n        return function (event) {\n            if (!event.currentTarget.disabled) {\n                return fig.toolbar_button_onmouseover(tooltip);\n            }\n        };\n    }\n\n    fig.buttons = {};\n    var buttonGroup = document.createElement('div');\n    buttonGroup.classList = 'mpl-button-group';\n    for (var toolbar_ind in mpl.toolbar_items) {\n        var name = mpl.toolbar_items[toolbar_ind][0];\n        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n        var image = mpl.toolbar_items[toolbar_ind][2];\n        var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n        if (!name) {\n            /* Instead of a spacer, we start a new button group. */\n            if (buttonGroup.hasChildNodes()) {\n                toolbar.appendChild(buttonGroup);\n            }\n            buttonGroup = document.createElement('div');\n            buttonGroup.classList = 'mpl-button-group';\n            continue;\n        }\n\n        var button = (fig.buttons[name] = document.createElement('button'));\n        button.classList = 'mpl-widget';\n        button.setAttribute('role', 'button');\n        button.setAttribute('aria-disabled', 'false');\n        button.addEventListener('click', on_click_closure(method_name));\n        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n\n        var icon_img = document.createElement('img');\n        icon_img.src = '_images/' + image + '.png';\n        icon_img.srcset = '_images/' + image + '_large.png 2x';\n        icon_img.alt = tooltip;\n        button.appendChild(icon_img);\n\n        buttonGroup.appendChild(button);\n    }\n\n    if (buttonGroup.hasChildNodes()) {\n        toolbar.appendChild(buttonGroup);\n    }\n\n    var fmt_picker = document.createElement('select');\n    fmt_picker.classList = 'mpl-widget';\n    toolbar.appendChild(fmt_picker);\n    this.format_dropdown = fmt_picker;\n\n    for (var ind in mpl.extensions) {\n        var fmt = mpl.extensions[ind];\n        var option = document.createElement('option');\n        option.selected = fmt === mpl.default_extension;\n        option.innerHTML = fmt;\n        fmt_picker.appendChild(option);\n    }\n\n    var status_bar = document.createElement('span');\n    status_bar.classList = 'mpl-message';\n    toolbar.appendChild(status_bar);\n    this.message = status_bar;\n};\n\nmpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n    // which will in turn request a refresh of the image.\n    this.send_message('resize', { width: x_pixels, height: y_pixels });\n};\n\nmpl.figure.prototype.send_message = function (type, properties) {\n    properties['type'] = type;\n    properties['figure_id'] = this.id;\n    this.ws.send(JSON.stringify(properties));\n};\n\nmpl.figure.prototype.send_draw_message = function () {\n    if (!this.waiting) {\n        this.waiting = true;\n        this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n    }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n    var format_dropdown = fig.format_dropdown;\n    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n    fig.ondownload(fig, format);\n};\n\nmpl.figure.prototype.handle_resize = function (fig, msg) {\n    var size = msg['size'];\n    if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n        fig._resize_canvas(size[0], size[1], msg['forward']);\n        fig.send_message('refresh', {});\n    }\n};\n\nmpl.figure.prototype.handle_rubberband = function (fig, msg) {\n    var x0 = msg['x0'] / fig.ratio;\n    var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n    var x1 = msg['x1'] / fig.ratio;\n    var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n    x0 = Math.floor(x0) + 0.5;\n    y0 = Math.floor(y0) + 0.5;\n    x1 = Math.floor(x1) + 0.5;\n    y1 = Math.floor(y1) + 0.5;\n    var min_x = Math.min(x0, x1);\n    var min_y = Math.min(y0, y1);\n    var width = Math.abs(x1 - x0);\n    var height = Math.abs(y1 - y0);\n\n    fig.rubberband_context.clearRect(\n        0,\n        0,\n        fig.canvas.width / fig.ratio,\n        fig.canvas.height / fig.ratio\n    );\n\n    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n};\n\nmpl.figure.prototype.handle_figure_label = function (fig, msg) {\n    // Updates the figure title.\n    fig.header.textContent = msg['label'];\n};\n\nmpl.figure.prototype.handle_cursor = function (fig, msg) {\n    fig.canvas_div.style.cursor = msg['cursor'];\n};\n\nmpl.figure.prototype.handle_message = function (fig, msg) {\n    fig.message.textContent = msg['message'];\n};\n\nmpl.figure.prototype.handle_draw = function (fig, _msg) {\n    // Request the server to send over a new figure.\n    fig.send_draw_message();\n};\n\nmpl.figure.prototype.handle_image_mode = function (fig, msg) {\n    fig.image_mode = msg['mode'];\n};\n\nmpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n    for (var key in msg) {\n        if (!(key in fig.buttons)) {\n            continue;\n        }\n        fig.buttons[key].disabled = !msg[key];\n        fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n    }\n};\n\nmpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n    if (msg['mode'] === 'PAN') {\n        fig.buttons['Pan'].classList.add('active');\n        fig.buttons['Zoom'].classList.remove('active');\n    } else if (msg['mode'] === 'ZOOM') {\n        fig.buttons['Pan'].classList.remove('active');\n        fig.buttons['Zoom'].classList.add('active');\n    } else {\n        fig.buttons['Pan'].classList.remove('active');\n        fig.buttons['Zoom'].classList.remove('active');\n    }\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n    // Called whenever the canvas gets updated.\n    this.send_message('ack', {});\n};\n\n// A function to construct a web socket function for onmessage handling.\n// Called in the figure constructor.\nmpl.figure.prototype._make_on_message_function = function (fig) {\n    return function socket_on_message(evt) {\n        if (evt.data instanceof Blob) {\n            var img = evt.data;\n            if (img.type !== 'image/png') {\n                /* FIXME: We get \"Resource interpreted as Image but\n                 * transferred with MIME type text/plain:\" errors on\n                 * Chrome.  But how to set the MIME type?  It doesn't seem\n                 * to be part of the websocket stream */\n                img.type = 'image/png';\n            }\n\n            /* Free the memory for the previous frames */\n            if (fig.imageObj.src) {\n                (window.URL || window.webkitURL).revokeObjectURL(\n                    fig.imageObj.src\n                );\n            }\n\n            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n                img\n            );\n            fig.updated_canvas_event();\n            fig.waiting = false;\n            return;\n        } else if (\n            typeof evt.data === 'string' &&\n            evt.data.slice(0, 21) === 'data:image/png;base64'\n        ) {\n            fig.imageObj.src = evt.data;\n            fig.updated_canvas_event();\n            fig.waiting = false;\n            return;\n        }\n\n        var msg = JSON.parse(evt.data);\n        var msg_type = msg['type'];\n\n        // Call the  \"handle_{type}\" callback, which takes\n        // the figure and JSON message as its only arguments.\n        try {\n            var callback = fig['handle_' + msg_type];\n        } catch (e) {\n            console.log(\n                \"No handler for the '\" + msg_type + \"' message type: \",\n                msg\n            );\n            return;\n        }\n\n        if (callback) {\n            try {\n                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n                callback(fig, msg);\n            } catch (e) {\n                console.log(\n                    \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n                    e,\n                    e.stack,\n                    msg\n                );\n            }\n        }\n    };\n};\n\nfunction getModifiers(event) {\n    var mods = [];\n    if (event.ctrlKey) {\n        mods.push('ctrl');\n    }\n    if (event.altKey) {\n        mods.push('alt');\n    }\n    if (event.shiftKey) {\n        mods.push('shift');\n    }\n    if (event.metaKey) {\n        mods.push('meta');\n    }\n    return mods;\n}\n\n/*\n * return a copy of an object with only non-object keys\n * we need this to avoid circular references\n * https://stackoverflow.com/a/24161582/3208463\n */\nfunction simpleKeys(original) {\n    return Object.keys(original).reduce(function (obj, key) {\n        if (typeof original[key] !== 'object') {\n            obj[key] = original[key];\n        }\n        return obj;\n    }, {});\n}\n\nmpl.figure.prototype.mouse_event = function (event, name) {\n    if (name === 'button_press') {\n        this.canvas.focus();\n        this.canvas_div.focus();\n    }\n\n    // from https://stackoverflow.com/q/1114465\n    var boundingRect = this.canvas.getBoundingClientRect();\n    var x = (event.clientX - boundingRect.left) * this.ratio;\n    var y = (event.clientY - boundingRect.top) * this.ratio;\n\n    this.send_message(name, {\n        x: x,\n        y: y,\n        button: event.button,\n        step: event.step,\n        modifiers: getModifiers(event),\n        guiEvent: simpleKeys(event),\n    });\n\n    return false;\n};\n\nmpl.figure.prototype._key_event_extra = function (_event, _name) {\n    // Handle any extra behaviour associated with a key event\n};\n\nmpl.figure.prototype.key_event = function (event, name) {\n    // Prevent repeat events\n    if (name === 'key_press') {\n        if (event.key === this._key) {\n            return;\n        } else {\n            this._key = event.key;\n        }\n    }\n    if (name === 'key_release') {\n        this._key = null;\n    }\n\n    var value = '';\n    if (event.ctrlKey && event.key !== 'Control') {\n        value += 'ctrl+';\n    }\n    else if (event.altKey && event.key !== 'Alt') {\n        value += 'alt+';\n    }\n    else if (event.shiftKey && event.key !== 'Shift') {\n        value += 'shift+';\n    }\n\n    value += 'k' + event.key;\n\n    this._key_event_extra(event, name);\n\n    this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n    return false;\n};\n\nmpl.figure.prototype.toolbar_button_onclick = function (name) {\n    if (name === 'download') {\n        this.handle_save(this, null);\n    } else {\n        this.send_message('toolbar_button', { name: name });\n    }\n};\n\nmpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n    this.message.textContent = tooltip;\n};\n\n///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n// prettier-ignore\nvar _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\nmpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\", \"webp\"];\n\nmpl.default_extension = \"png\";/* global mpl */\n\nvar comm_websocket_adapter = function (comm) {\n    // Create a \"websocket\"-like object which calls the given IPython comm\n    // object with the appropriate methods. Currently this is a non binary\n    // socket, so there is still some room for performance tuning.\n    var ws = {};\n\n    ws.binaryType = comm.kernel.ws.binaryType;\n    ws.readyState = comm.kernel.ws.readyState;\n    function updateReadyState(_event) {\n        if (comm.kernel.ws) {\n            ws.readyState = comm.kernel.ws.readyState;\n        } else {\n            ws.readyState = 3; // Closed state.\n        }\n    }\n    comm.kernel.ws.addEventListener('open', updateReadyState);\n    comm.kernel.ws.addEventListener('close', updateReadyState);\n    comm.kernel.ws.addEventListener('error', updateReadyState);\n\n    ws.close = function () {\n        comm.close();\n    };\n    ws.send = function (m) {\n        //console.log('sending', m);\n        comm.send(m);\n    };\n    // Register the callback with on_msg.\n    comm.on_msg(function (msg) {\n        //console.log('receiving', msg['content']['data'], msg);\n        var data = msg['content']['data'];\n        if (data['blob'] !== undefined) {\n            data = {\n                data: new Blob(msg['buffers'], { type: data['blob'] }),\n            };\n        }\n        // Pass the mpl event to the overridden (by mpl) onmessage function.\n        ws.onmessage(data);\n    });\n    return ws;\n};\n\nmpl.mpl_figure_comm = function (comm, msg) {\n    // This is the function which gets called when the mpl process\n    // starts-up an IPython Comm through the \"matplotlib\" channel.\n\n    var id = msg.content.data.id;\n    // Get hold of the div created by the display call when the Comm\n    // socket was opened in Python.\n    var element = document.getElementById(id);\n    var ws_proxy = comm_websocket_adapter(comm);\n\n    function ondownload(figure, _format) {\n        window.open(figure.canvas.toDataURL());\n    }\n\n    var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n\n    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n    // web socket which is closed, not our websocket->open comm proxy.\n    ws_proxy.onopen();\n\n    fig.parent_element = element;\n    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n    if (!fig.cell_info) {\n        console.error('Failed to find cell for figure', id, fig);\n        return;\n    }\n    fig.cell_info[0].output_area.element.on(\n        'cleared',\n        { fig: fig },\n        fig._remove_fig_handler\n    );\n};\n\nmpl.figure.prototype.handle_close = function (fig, msg) {\n    var width = fig.canvas.width / fig.ratio;\n    fig.cell_info[0].output_area.element.off(\n        'cleared',\n        fig._remove_fig_handler\n    );\n    fig.resizeObserverInstance.unobserve(fig.canvas_div);\n\n    // Update the output cell to use the data from the current canvas.\n    fig.push_to_output();\n    var dataURL = fig.canvas.toDataURL();\n    // Re-enable the keyboard manager in IPython - without this line, in FF,\n    // the notebook keyboard shortcuts fail.\n    IPython.keyboard_manager.enable();\n    fig.parent_element.innerHTML =\n        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n    fig.close_ws(fig, msg);\n};\n\nmpl.figure.prototype.close_ws = function (fig, msg) {\n    fig.send_message('closing', msg);\n    // fig.ws.close()\n};\n\nmpl.figure.prototype.push_to_output = function (_remove_interactive) {\n    // Turn the data on the canvas into data in the output cell.\n    var width = this.canvas.width / this.ratio;\n    var dataURL = this.canvas.toDataURL();\n    this.cell_info[1]['text/html'] =\n        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n    // Tell IPython that the notebook contents must change.\n    IPython.notebook.set_dirty(true);\n    this.send_message('ack', {});\n    var fig = this;\n    // Wait a second, then push the new image to the DOM so\n    // that it is saved nicely (might be nice to debounce this).\n    setTimeout(function () {\n        fig.push_to_output();\n    }, 1000);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n    var fig = this;\n\n    var toolbar = document.createElement('div');\n    toolbar.classList = 'btn-toolbar';\n    this.root.appendChild(toolbar);\n\n    function on_click_closure(name) {\n        return function (_event) {\n            return fig.toolbar_button_onclick(name);\n        };\n    }\n\n    function on_mouseover_closure(tooltip) {\n        return function (event) {\n            if (!event.currentTarget.disabled) {\n                return fig.toolbar_button_onmouseover(tooltip);\n            }\n        };\n    }\n\n    fig.buttons = {};\n    var buttonGroup = document.createElement('div');\n    buttonGroup.classList = 'btn-group';\n    var button;\n    for (var toolbar_ind in mpl.toolbar_items) {\n        var name = mpl.toolbar_items[toolbar_ind][0];\n        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n        var image = mpl.toolbar_items[toolbar_ind][2];\n        var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n        if (!name) {\n            /* Instead of a spacer, we start a new button group. */\n            if (buttonGroup.hasChildNodes()) {\n                toolbar.appendChild(buttonGroup);\n            }\n            buttonGroup = document.createElement('div');\n            buttonGroup.classList = 'btn-group';\n            continue;\n        }\n\n        button = fig.buttons[name] = document.createElement('button');\n        button.classList = 'btn btn-default';\n        button.href = '#';\n        button.title = name;\n        button.innerHTML = '<i class=\"fa ' + image + ' fa-lg\"></i>';\n        button.addEventListener('click', on_click_closure(method_name));\n        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n        buttonGroup.appendChild(button);\n    }\n\n    if (buttonGroup.hasChildNodes()) {\n        toolbar.appendChild(buttonGroup);\n    }\n\n    // Add the status bar.\n    var status_bar = document.createElement('span');\n    status_bar.classList = 'mpl-message pull-right';\n    toolbar.appendChild(status_bar);\n    this.message = status_bar;\n\n    // Add the close button to the window.\n    var buttongrp = document.createElement('div');\n    buttongrp.classList = 'btn-group inline pull-right';\n    button = document.createElement('button');\n    button.classList = 'btn btn-mini btn-primary';\n    button.href = '#';\n    button.title = 'Stop Interaction';\n    button.innerHTML = '<i class=\"fa fa-power-off icon-remove icon-large\"></i>';\n    button.addEventListener('click', function (_evt) {\n        fig.handle_close(fig, {});\n    });\n    button.addEventListener(\n        'mouseover',\n        on_mouseover_closure('Stop Interaction')\n    );\n    buttongrp.appendChild(button);\n    var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n    titlebar.insertBefore(buttongrp, titlebar.firstChild);\n};\n\nmpl.figure.prototype._remove_fig_handler = function (event) {\n    var fig = event.data.fig;\n    if (event.target !== this) {\n        // Ignore bubbled events from children.\n        return;\n    }\n    fig.close_ws(fig, {});\n};\n\nmpl.figure.prototype._root_extra_style = function (el) {\n    el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n};\n\nmpl.figure.prototype._canvas_extra_style = function (el) {\n    // this is important to make the div 'focusable\n    el.setAttribute('tabindex', 0);\n    // reach out to IPython and tell the keyboard manager to turn it's self\n    // off when our div gets focus\n\n    // location in version 3\n    if (IPython.notebook.keyboard_manager) {\n        IPython.notebook.keyboard_manager.register_events(el);\n    } else {\n        // location in version 2\n        IPython.keyboard_manager.register_events(el);\n    }\n};\n\nmpl.figure.prototype._key_event_extra = function (event, _name) {\n    // Check for shift+enter\n    if (event.shiftKey && event.which === 13) {\n        this.canvas_div.blur();\n        // select the cell after this one\n        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n        IPython.notebook.select(index + 1);\n    }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n    fig.ondownload(fig, null);\n};\n\nmpl.find_output_cell = function (html_output) {\n    // Return the cell and output element which can be found *uniquely* in the notebook.\n    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n    // IPython event is triggered only after the cells have been serialised, which for\n    // our purposes (turning an active figure into a static one), is too late.\n    var cells = IPython.notebook.get_cells();\n    var ncells = cells.length;\n    for (var i = 0; i < ncells; i++) {\n        var cell = cells[i];\n        if (cell.cell_type === 'code') {\n            for (var j = 0; j < cell.output_area.outputs.length; j++) {\n                var data = cell.output_area.outputs[j];\n                if (data.data) {\n                    // IPython >= 3 moved mimebundle to data attribute of output\n                    data = data.data;\n                }\n                if (data['text/html'] === html_output) {\n                    return [cell, data, j];\n                }\n            }\n        }\n    }\n};\n\n// Register the function which deals with the matplotlib target/channel.\n// The kernel may be null if the page has been refreshed.\nif (IPython.notebook.kernel !== null) {\n    IPython.notebook.kernel.comm_manager.register_target(\n        'matplotlib',\n        mpl.mpl_figure_comm\n    );\n}\n",
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div id='d4d94922-9f92-4d20-8b80-04c361ade104'></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from __future__ import (absolute_import, division, print_function,\n",
    "                        unicode_literals)\n",
    "\n",
    "import datetime  # For datetime objects\n",
    "import os.path  # To manage paths\n",
    "import sys  # To find out the script name (in argv[0])\n",
    "\n",
    "# Import the backtrader platform\n",
    "import backtrader as bt\n",
    "\n",
    "\n",
    "# =================Create a Stratey=============================================================\n",
    "class TestStrategy(bt.Strategy):\n",
    "\n",
    "    def log(self, txt, dt=None):\n",
    "        ''' Logging function fot this strategy'''\n",
    "        dt = dt or self.datas[0].datetime.date(0)\n",
    "        print('%s, %s' % (dt.isoformat(), txt))\n",
    "\n",
    "    def __init__(self):\n",
    "        # Keep a reference to the \"close\" line in the data[0] dataseries\n",
    "        self.dataclose = self.datas[0].close\n",
    "\n",
    "        # To keep track of pending orders\n",
    "        self.order = None\n",
    "\n",
    "    def notify_order(self, order):\n",
    "        # in 关键字用于检查 order.status 是否在给定的列表 [order.Submitted, order.Accepted] 中。\n",
    "        if order.status in [order.Submitted, order.Accepted]:\n",
    "            # Buy/Sell order submitted/accepted to/by broker - Nothing to do\n",
    "            # 如果订单状态是已提交或已接受，那么不需要做任何事情。\n",
    "            return # 这通常是因为在订单提交或接受的状态下，还没有发生实际的交易，因此不需要进行额外的处理。\n",
    "\n",
    "        # Check if an order has been completed\n",
    "        # Attention: broker could reject order if not enough cash\n",
    "        if order.status in [order.Completed]:# 如果订单状态是已完成，表示订单已经执行。\n",
    "            if order.isbuy():\n",
    "                self.log('BUY EXECUTED, %.2f' % order.executed.price)\n",
    "            elif order.issell():\n",
    "                self.log('SELL EXECUTED, %.2f' % order.executed.price)\n",
    "            # 记录订单执行时的柱线数量（即当前的柱线数量）。len(self) 返回当前的柱线数量。\n",
    "            self.bar_executed = len(self)\n",
    "\n",
    "        elif order.status in [order.Canceled, order.Margin, order.Rejected]:\n",
    "            self.log('Order Canceled/Margin/Rejected')\n",
    "\n",
    "        # Write down: no pending order\n",
    "        # 无论订单状态如何，最后都将 self.order 设置为 None，表示没有未决订单\n",
    "        self.order = None\n",
    "\n",
    "    def next(self):\n",
    "        # Simply log the closing price of the series from the reference\n",
    "        self.log('Close, %.2f' % self.dataclose[0])\n",
    "\n",
    "        # Check if an order is pending ... if yes, we cannot send a 2nd one\n",
    "        #这行代码检查是否有未决订单（即 self.order 是否为 None）。\n",
    "        # 如果有未决订单，则直接返回，不再发送新的订单。\n",
    "        if self.order:\n",
    "            return\n",
    "\n",
    "        # Check if we are in the market\n",
    "        # 如果没有持仓，则执行下面的买入逻辑。\n",
    "        if not self.position:\n",
    "\n",
    "            # Not yet ... we MIGHT BUY if ...\n",
    "            if self.dataclose[0] < self.dataclose[-1]:\n",
    "                    # current close less than previous close\n",
    "\n",
    "                    if self.dataclose[-1] < self.dataclose[-2]:\n",
    "                        # previous close less than the previous close\n",
    "\n",
    "                        # BUY, BUY, BUY!!! (with default parameters)\n",
    "                        self.log('BUY CREATE, %.2f' % self.dataclose[0])\n",
    "\n",
    "                        # Keep track of the created order to avoid a 2nd order\n",
    "                        # 创建一个买入订单，并将订单对象赋值给 self.order，以避免重复下单。\n",
    "                        self.order = self.buy()\n",
    "\n",
    "        else:\n",
    "\n",
    "            # Already in the market ... we might sell\n",
    "            # 检查当前的柱线数量是否大于或等于买入执行时的柱线数量加上 5。\n",
    "            # 如果是，则认为是一个卖出信号。\n",
    "            \n",
    "            # 在一个回测框架中，策略实例会在每个新的交易日（或每个新的数据点）被调用一次 next 方法。\n",
    "            # 每次调用 next 方法时，len(self) 的值就会增加1，表示策略已经处理过的柱线数量。\n",
    "            \n",
    "            # self.bar_executed 的作用是记录买入或卖出订单执行时的柱线数量，以便在后续的策略逻辑中使用。\n",
    "            if len(self) >= (self.bar_executed + 5):\n",
    "                # SELL, SELL, SELL!!! (with all possible default parameters)\n",
    "                self.log('SELL CREATE, %.2f' % self.dataclose[0])\n",
    "\n",
    "                # Keep track of the created order to avoid a 2nd order\n",
    "                self.order = self.sell()\n",
    "# ============================================================================================\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    # Create a cerebro entity\n",
    "    cerebro = bt.Cerebro()\n",
    "\n",
    "    # Add a strategy\n",
    "    cerebro.addstrategy(TestStrategy)\n",
    "\n",
    "    # Datas are in a subfolder of the samples. Need to find where the script is\n",
    "    # because it could have been called from anywhere\n",
    "    # modpath = os.path.dirname(os.path.abspath(sys.argv[0]))\n",
    "    modpath = 'C:\\\\Users\\\\bww\\gitee\\\\stock\\\\backtrader'\n",
    "    datapath = os.path.join(modpath, '000001.SS.csv')\n",
    "\n",
    "    # Create a Data Feed\n",
    "    data = bt.feeds.YahooFinanceCSVData(\n",
    "        dataname=datapath,\n",
    "        # Do not pass values before this date\n",
    "        fromdate=datetime.datetime(2000, 1, 1),\n",
    "        # Do not pass values before this date\n",
    "        todate=datetime.datetime(2024, 12, 31),\n",
    "        # Do not pass values after this date\n",
    "        reverse=False)\n",
    "\n",
    "    # Add the Data Feed to Cerebro\n",
    "    cerebro.adddata(data)\n",
    "\n",
    "    # Set our desired cash start\n",
    "    cerebro.broker.setcash(100000.0)\n",
    "\n",
    "    # Print out the starting conditions\n",
    "    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())\n",
    "\n",
    "    # Run over everything\n",
    "    cerebro.run()\n",
    "\n",
    "    # Print out the final result\n",
    "    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())\n",
    "    \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# The broker says: Show me the money!\n",
    "让我们为每次操作添加合理的 0.1% 佣金\n",
    "cerebro.broker.setcommission(commission=0.001)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Starting Portfolio Value: 100000.00\n",
      "2023-06-19, Close, 3255.81\n",
      "2023-06-20, Close, 3240.36\n",
      "2023-06-21, Close, 3197.90\n",
      "2023-06-21, BUY CREATE, 3197.90\n",
      "2023-06-26, BUY EXECUTED, Price: 3177.23, Cost: 3177.23, Comm 3.18\n",
      "2023-06-26, Close, 3150.62\n",
      "2023-06-27, Close, 3189.44\n",
      "2023-06-28, Close, 3189.38\n",
      "2023-06-29, Close, 3182.38\n",
      "2023-06-30, Close, 3202.06\n",
      "2023-07-03, Close, 3243.98\n",
      "2023-07-03, SELL CREATE, 3243.98\n",
      "2023-07-04, SELL EXECUTED, Price: 3241.23, Cost: 3177.23, Comm 3.24\n",
      "2023-07-04, OPERATION PROFIT, GROSS 64.00, NET 57.58\n",
      "2023-07-04, Close, 3245.35\n",
      "2023-07-05, Close, 3222.95\n",
      "2023-07-06, Close, 3205.57\n",
      "2023-07-06, BUY CREATE, 3205.57\n",
      "2023-07-07, BUY EXECUTED, Price: 3197.47, Cost: 3197.47, Comm 3.20\n",
      "2023-07-07, Close, 3196.61\n",
      "2023-07-10, Close, 3203.70\n",
      "2023-07-11, Close, 3221.37\n",
      "2023-07-12, Close, 3196.13\n",
      "2023-07-13, Close, 3236.48\n",
      "2023-07-14, Close, 3237.70\n",
      "2023-07-14, SELL CREATE, 3237.70\n",
      "2023-07-17, SELL EXECUTED, Price: 3219.02, Cost: 3197.47, Comm 3.22\n",
      "2023-07-17, OPERATION PROFIT, GROSS 21.55, NET 15.13\n",
      "2023-07-17, Close, 3209.63\n",
      "2023-07-18, Close, 3197.82\n",
      "2023-07-18, BUY CREATE, 3197.82\n",
      "2023-07-19, BUY EXECUTED, Price: 3195.01, Cost: 3195.01, Comm 3.20\n",
      "2023-07-19, Close, 3198.84\n",
      "2023-07-20, Close, 3169.52\n",
      "2023-07-21, Close, 3167.75\n",
      "2023-07-24, Close, 3164.16\n",
      "2023-07-25, Close, 3231.52\n",
      "2023-07-26, Close, 3223.03\n",
      "2023-07-26, SELL CREATE, 3223.03\n",
      "2023-07-27, SELL EXECUTED, Price: 3225.48, Cost: 3195.01, Comm 3.23\n",
      "2023-07-27, OPERATION PROFIT, GROSS 30.47, NET 24.05\n",
      "2023-07-27, Close, 3216.67\n",
      "2023-07-27, BUY CREATE, 3216.67\n",
      "2023-07-28, BUY EXECUTED, Price: 3206.74, Cost: 3206.74, Comm 3.21\n",
      "2023-07-28, Close, 3275.93\n",
      "2023-07-31, Close, 3291.04\n",
      "2023-08-01, Close, 3290.95\n",
      "2023-08-02, Close, 3261.69\n",
      "2023-08-03, Close, 3280.46\n",
      "2023-08-04, Close, 3288.08\n",
      "2023-08-04, SELL CREATE, 3288.08\n",
      "2023-08-07, SELL EXECUTED, Price: 3276.78, Cost: 3206.74, Comm 3.28\n",
      "2023-08-07, OPERATION PROFIT, GROSS 70.04, NET 63.56\n",
      "2023-08-07, Close, 3268.83\n",
      "2023-08-08, Close, 3260.62\n",
      "2023-08-08, BUY CREATE, 3260.62\n",
      "2023-08-09, BUY EXECUTED, Price: 3251.15, Cost: 3251.15, Comm 3.25\n",
      "2023-08-09, Close, 3244.49\n",
      "2023-08-10, Close, 3254.56\n",
      "2023-08-11, Close, 3189.25\n",
      "2023-08-14, Close, 3178.43\n",
      "2023-08-15, Close, 3176.18\n",
      "2023-08-16, Close, 3150.13\n",
      "2023-08-16, SELL CREATE, 3150.13\n",
      "2023-08-17, SELL EXECUTED, Price: 3137.35, Cost: 3251.15, Comm 3.14\n",
      "2023-08-17, OPERATION PROFIT, GROSS -113.80, NET -120.19\n",
      "2023-08-17, Close, 3163.74\n",
      "2023-08-18, Close, 3131.95\n",
      "2023-08-21, Close, 3092.98\n",
      "2023-08-21, BUY CREATE, 3092.98\n",
      "2023-08-22, BUY EXECUTED, Price: 3103.78, Cost: 3103.78, Comm 3.10\n",
      "2023-08-22, Close, 3120.33\n",
      "2023-08-23, Close, 3078.40\n",
      "2023-08-24, Close, 3082.24\n",
      "2023-08-25, Close, 3064.07\n",
      "2023-08-28, Close, 3098.64\n",
      "2023-08-29, Close, 3135.89\n",
      "2023-08-29, SELL CREATE, 3135.89\n",
      "2023-08-30, SELL EXECUTED, Price: 3144.41, Cost: 3103.78, Comm 3.14\n",
      "2023-08-30, OPERATION PROFIT, GROSS 40.63, NET 34.38\n",
      "2023-08-30, Close, 3137.14\n",
      "2023-08-31, Close, 3119.88\n",
      "2023-09-01, Close, 3133.25\n",
      "2023-09-04, Close, 3177.06\n",
      "2023-09-05, Close, 3154.37\n",
      "2023-09-06, Close, 3158.08\n",
      "2023-09-07, Close, 3122.35\n",
      "2023-09-08, Close, 3116.72\n",
      "2023-09-08, BUY CREATE, 3116.72\n",
      "2023-09-11, BUY EXECUTED, Price: 3119.61, Cost: 3119.61, Comm 3.12\n",
      "2023-09-11, Close, 3142.78\n",
      "2023-09-12, Close, 3137.06\n",
      "2023-09-13, Close, 3123.07\n",
      "2023-09-14, Close, 3126.55\n",
      "2023-09-15, Close, 3117.74\n",
      "2023-09-18, Close, 3125.93\n",
      "2023-09-18, SELL CREATE, 3125.93\n",
      "2023-09-19, SELL EXECUTED, Price: 3123.99, Cost: 3119.61, Comm 3.12\n",
      "2023-09-19, OPERATION PROFIT, GROSS 4.38, NET -1.86\n",
      "2023-09-19, Close, 3124.96\n",
      "2023-09-20, Close, 3108.57\n",
      "2023-09-20, BUY CREATE, 3108.57\n",
      "2023-09-21, BUY EXECUTED, Price: 3103.18, Cost: 3103.18, Comm 3.10\n",
      "2023-09-21, Close, 3084.70\n",
      "2023-09-22, Close, 3132.43\n",
      "2023-09-25, Close, 3115.61\n",
      "2023-09-26, Close, 3102.27\n",
      "2023-09-27, Close, 3107.32\n",
      "2023-09-28, Close, 3110.48\n",
      "2023-09-28, SELL CREATE, 3110.48\n",
      "2023-10-09, SELL EXECUTED, Price: 3100.00, Cost: 3103.18, Comm 3.10\n",
      "2023-10-09, OPERATION PROFIT, GROSS -3.18, NET -9.38\n",
      "2023-10-09, Close, 3096.92\n",
      "2023-10-10, Close, 3075.24\n",
      "2023-10-10, BUY CREATE, 3075.24\n",
      "2023-10-11, BUY EXECUTED, Price: 3085.70, Cost: 3085.70, Comm 3.09\n",
      "2023-10-11, Close, 3078.96\n",
      "2023-10-12, Close, 3107.90\n",
      "2023-10-13, Close, 3088.10\n",
      "2023-10-16, Close, 3073.81\n",
      "2023-10-17, Close, 3083.50\n",
      "2023-10-18, Close, 3058.71\n",
      "2023-10-18, SELL CREATE, 3058.71\n",
      "2023-10-19, SELL EXECUTED, Price: 3043.20, Cost: 3085.70, Comm 3.04\n",
      "2023-10-19, OPERATION PROFIT, GROSS -42.50, NET -48.63\n",
      "2023-10-19, Close, 3005.39\n",
      "2023-10-19, BUY CREATE, 3005.39\n",
      "2023-10-20, BUY EXECUTED, Price: 2995.36, Cost: 2995.36, Comm 3.00\n",
      "2023-10-20, Close, 2983.06\n",
      "2023-10-23, Close, 2939.29\n",
      "2023-10-24, Close, 2962.24\n",
      "2023-10-25, Close, 2974.11\n",
      "2023-10-26, Close, 2988.30\n",
      "2023-10-27, Close, 3017.78\n",
      "2023-10-27, SELL CREATE, 3017.78\n",
      "2023-10-30, SELL EXECUTED, Price: 3009.61, Cost: 2995.36, Comm 3.01\n",
      "2023-10-30, OPERATION PROFIT, GROSS 14.25, NET 8.25\n",
      "2023-10-30, Close, 3021.55\n",
      "2023-10-31, Close, 3018.77\n",
      "2023-11-01, Close, 3023.08\n",
      "2023-11-02, Close, 3009.41\n",
      "2023-11-03, Close, 3030.80\n",
      "2023-11-06, Close, 3058.41\n",
      "2023-11-07, Close, 3057.27\n",
      "2023-11-08, Close, 3052.37\n",
      "2023-11-08, BUY CREATE, 3052.37\n",
      "2023-11-09, BUY EXECUTED, Price: 3047.65, Cost: 3047.65, Comm 3.05\n",
      "2023-11-09, Close, 3053.28\n",
      "2023-11-10, Close, 3038.97\n",
      "2023-11-13, Close, 3046.53\n",
      "2023-11-14, Close, 3056.07\n",
      "2023-11-15, Close, 3072.83\n",
      "2023-11-16, Close, 3050.93\n",
      "2023-11-16, SELL CREATE, 3050.93\n",
      "2023-11-17, SELL EXECUTED, Price: 3043.62, Cost: 3047.65, Comm 3.04\n",
      "2023-11-17, OPERATION PROFIT, GROSS -4.03, NET -10.12\n",
      "2023-11-17, Close, 3054.37\n",
      "2023-11-20, Close, 3068.32\n",
      "2023-11-21, Close, 3067.93\n",
      "2023-11-22, Close, 3043.61\n",
      "2023-11-22, BUY CREATE, 3043.61\n",
      "2023-11-23, BUY EXECUTED, Price: 3041.68, Cost: 3041.68, Comm 3.04\n",
      "2023-11-23, Close, 3061.86\n",
      "2023-11-24, Close, 3040.97\n",
      "2023-11-27, Close, 3031.70\n",
      "2023-11-28, Close, 3038.55\n",
      "2023-11-29, Close, 3021.69\n",
      "2023-11-30, Close, 3029.67\n",
      "2023-11-30, SELL CREATE, 3029.67\n",
      "2023-12-01, SELL EXECUTED, Price: 3027.35, Cost: 3041.68, Comm 3.03\n",
      "2023-12-01, OPERATION PROFIT, GROSS -14.33, NET -20.40\n",
      "2023-12-01, Close, 3031.64\n",
      "2023-12-04, Close, 3022.91\n",
      "2023-12-05, Close, 2972.30\n",
      "2023-12-05, BUY CREATE, 2972.30\n",
      "2023-12-06, BUY EXECUTED, Price: 2966.95, Cost: 2966.95, Comm 2.97\n",
      "2023-12-06, Close, 2968.93\n",
      "2023-12-07, Close, 2966.21\n",
      "2023-12-08, Close, 2969.56\n",
      "2023-12-11, Close, 2991.44\n",
      "2023-12-12, Close, 3003.44\n",
      "2023-12-13, Close, 2968.76\n",
      "2023-12-13, SELL CREATE, 2968.76\n",
      "2023-12-14, SELL EXECUTED, Price: 2979.70, Cost: 2966.95, Comm 2.98\n",
      "2023-12-14, OPERATION PROFIT, GROSS 12.75, NET 6.80\n",
      "2023-12-14, Close, 2958.99\n",
      "2023-12-14, BUY CREATE, 2958.99\n",
      "2023-12-15, BUY EXECUTED, Price: 2966.30, Cost: 2966.30, Comm 2.97\n",
      "2023-12-15, Close, 2942.56\n",
      "2023-12-18, Close, 2930.80\n",
      "2023-12-19, Close, 2932.39\n",
      "2023-12-20, Close, 2902.11\n",
      "2023-12-21, Close, 2918.72\n",
      "2023-12-22, Close, 2914.77\n",
      "2023-12-22, SELL CREATE, 2914.77\n",
      "2023-12-25, SELL EXECUTED, Price: 2909.88, Cost: 2966.30, Comm 2.91\n",
      "2023-12-25, OPERATION PROFIT, GROSS -56.42, NET -62.30\n",
      "2023-12-25, Close, 2918.81\n",
      "2023-12-26, Close, 2898.88\n",
      "2023-12-27, Close, 2914.61\n",
      "2023-12-28, Close, 2954.70\n",
      "2023-12-29, Close, 2974.94\n",
      "2024-01-02, Close, 2962.28\n",
      "2024-01-03, Close, 2967.25\n",
      "2024-01-04, Close, 2954.35\n",
      "2024-01-05, Close, 2929.18\n",
      "2024-01-05, BUY CREATE, 2929.18\n",
      "2024-01-08, BUY EXECUTED, Price: 2923.46, Cost: 2923.46, Comm 2.92\n",
      "2024-01-08, Close, 2887.54\n",
      "2024-01-09, Close, 2893.25\n",
      "2024-01-10, Close, 2877.70\n",
      "2024-01-11, Close, 2886.65\n",
      "2024-01-12, Close, 2881.98\n",
      "2024-01-15, Close, 2886.29\n",
      "2024-01-15, SELL CREATE, 2886.29\n",
      "2024-01-16, SELL EXECUTED, Price: 2881.56, Cost: 2923.46, Comm 2.88\n",
      "2024-01-16, OPERATION PROFIT, GROSS -41.90, NET -47.71\n",
      "2024-01-16, Close, 2893.99\n",
      "2024-01-17, Close, 2833.61\n",
      "2024-01-18, Close, 2845.78\n",
      "2024-01-19, Close, 2832.28\n",
      "2024-01-22, Close, 2756.34\n",
      "2024-01-22, BUY CREATE, 2756.34\n",
      "2024-01-23, BUY EXECUTED, Price: 2746.89, Cost: 2746.89, Comm 2.75\n",
      "2024-01-23, Close, 2770.98\n",
      "2024-01-24, Close, 2820.77\n",
      "2024-01-25, Close, 2906.11\n",
      "2024-01-26, Close, 2910.22\n",
      "2024-01-29, Close, 2883.36\n",
      "2024-01-30, Close, 2830.53\n",
      "2024-01-30, SELL CREATE, 2830.53\n",
      "2024-01-31, SELL EXECUTED, Price: 2815.50, Cost: 2746.89, Comm 2.82\n",
      "2024-01-31, OPERATION PROFIT, GROSS 68.61, NET 63.05\n",
      "2024-01-31, Close, 2788.55\n",
      "2024-01-31, BUY CREATE, 2788.55\n",
      "2024-02-01, BUY EXECUTED, Price: 2773.42, Cost: 2773.42, Comm 2.77\n",
      "2024-02-01, Close, 2770.74\n",
      "2024-02-02, Close, 2730.15\n",
      "2024-02-05, Close, 2702.19\n",
      "2024-02-06, Close, 2789.49\n",
      "2024-02-07, Close, 2829.70\n",
      "2024-02-08, Close, 2865.90\n",
      "2024-02-08, SELL CREATE, 2865.90\n",
      "2024-02-19, SELL EXECUTED, Price: 2886.59, Cost: 2773.42, Comm 2.89\n",
      "2024-02-19, OPERATION PROFIT, GROSS 113.17, NET 107.51\n",
      "2024-02-19, Close, 2910.54\n",
      "2024-02-20, Close, 2922.73\n",
      "2024-02-21, Close, 2950.96\n",
      "2024-02-22, Close, 2988.36\n",
      "2024-02-23, Close, 3004.88\n",
      "2024-02-26, Close, 2977.02\n",
      "2024-02-27, Close, 3015.48\n",
      "2024-02-28, Close, 2957.85\n",
      "2024-02-29, Close, 3015.17\n",
      "2024-03-01, Close, 3027.02\n",
      "2024-03-04, Close, 3039.31\n",
      "2024-03-05, Close, 3047.79\n",
      "2024-03-06, Close, 3039.93\n",
      "2024-03-07, Close, 3027.40\n",
      "2024-03-07, BUY CREATE, 3027.40\n",
      "2024-03-08, BUY EXECUTED, Price: 3026.17, Cost: 3026.17, Comm 3.03\n",
      "2024-03-08, Close, 3046.02\n",
      "2024-03-11, Close, 3068.46\n",
      "2024-03-12, Close, 3055.94\n",
      "2024-03-13, Close, 3043.83\n",
      "2024-03-14, Close, 3038.23\n",
      "2024-03-15, Close, 3054.64\n",
      "2024-03-15, SELL CREATE, 3054.64\n",
      "2024-03-18, SELL EXECUTED, Price: 3057.21, Cost: 3026.17, Comm 3.06\n",
      "2024-03-18, OPERATION PROFIT, GROSS 31.04, NET 24.96\n",
      "2024-03-18, Close, 3084.93\n",
      "2024-03-19, Close, 3062.76\n",
      "2024-03-20, Close, 3079.69\n",
      "2024-03-21, Close, 3077.11\n",
      "2024-03-22, Close, 3048.03\n",
      "2024-03-22, BUY CREATE, 3048.03\n",
      "2024-03-25, BUY EXECUTED, Price: 3040.50, Cost: 3040.50, Comm 3.04\n",
      "2024-03-25, Close, 3026.31\n",
      "2024-03-26, Close, 3031.48\n",
      "2024-03-27, Close, 2993.14\n",
      "2024-03-28, Close, 3010.66\n",
      "2024-03-29, Close, 3041.17\n",
      "2024-04-01, Close, 3077.38\n",
      "2024-04-01, SELL CREATE, 3077.38\n",
      "2024-04-02, SELL EXECUTED, Price: 3076.88, Cost: 3040.50, Comm 3.08\n",
      "2024-04-02, OPERATION PROFIT, GROSS 36.38, NET 30.26\n",
      "2024-04-02, Close, 3074.96\n",
      "2024-04-03, Close, 3069.30\n",
      "2024-04-03, BUY CREATE, 3069.30\n",
      "2024-04-08, BUY EXECUTED, Price: 3057.33, Cost: 3057.33, Comm 3.06\n",
      "2024-04-08, Close, 3047.05\n",
      "2024-04-09, Close, 3048.54\n",
      "2024-04-10, Close, 3027.33\n",
      "2024-04-11, Close, 3034.25\n",
      "2024-04-12, Close, 3019.47\n",
      "2024-04-15, Close, 3057.38\n",
      "2024-04-15, SELL CREATE, 3057.38\n",
      "2024-04-16, SELL EXECUTED, Price: 3043.55, Cost: 3057.33, Comm 3.04\n",
      "2024-04-16, OPERATION PROFIT, GROSS -13.78, NET -19.88\n",
      "2024-04-16, Close, 3007.07\n",
      "2024-04-17, Close, 3071.38\n",
      "2024-04-18, Close, 3074.23\n",
      "2024-04-19, Close, 3065.26\n",
      "2024-04-22, Close, 3044.59\n",
      "2024-04-22, BUY CREATE, 3044.59\n",
      "2024-04-23, BUY EXECUTED, Price: 3038.61, Cost: 3038.61, Comm 3.04\n",
      "2024-04-23, Close, 3021.98\n",
      "2024-04-24, Close, 3044.82\n",
      "2024-04-25, Close, 3052.90\n",
      "2024-04-26, Close, 3088.64\n",
      "2024-04-29, Close, 3113.04\n",
      "2024-04-30, Close, 3104.82\n",
      "2024-04-30, SELL CREATE, 3104.82\n",
      "2024-05-06, SELL EXECUTED, Price: 3132.51, Cost: 3038.61, Comm 3.13\n",
      "2024-05-06, OPERATION PROFIT, GROSS 93.90, NET 87.73\n",
      "2024-05-06, Close, 3140.72\n",
      "2024-05-07, Close, 3147.74\n",
      "2024-05-08, Close, 3128.48\n",
      "2024-05-09, Close, 3154.32\n",
      "2024-05-10, Close, 3154.55\n",
      "2024-05-13, Close, 3148.02\n",
      "2024-05-14, Close, 3145.77\n",
      "2024-05-14, BUY CREATE, 3145.77\n",
      "2024-05-15, BUY EXECUTED, Price: 3141.25, Cost: 3141.25, Comm 3.14\n",
      "2024-05-15, Close, 3119.90\n",
      "2024-05-16, Close, 3122.40\n",
      "2024-05-17, Close, 3154.03\n",
      "2024-05-20, Close, 3171.15\n",
      "2024-05-21, Close, 3157.97\n",
      "2024-05-22, Close, 3158.54\n",
      "2024-05-22, SELL CREATE, 3158.54\n",
      "2024-05-23, SELL EXECUTED, Price: 3149.88, Cost: 3141.25, Comm 3.15\n",
      "2024-05-23, OPERATION PROFIT, GROSS 8.63, NET 2.34\n",
      "2024-05-23, Close, 3116.39\n",
      "2024-05-24, Close, 3088.87\n",
      "2024-05-24, BUY CREATE, 3088.87\n",
      "2024-05-27, BUY EXECUTED, Price: 3094.14, Cost: 3094.14, Comm 3.09\n",
      "2024-05-27, Close, 3124.04\n",
      "2024-05-28, Close, 3109.57\n",
      "2024-05-29, Close, 3111.02\n",
      "2024-05-30, Close, 3091.68\n",
      "2024-05-31, Close, 3086.81\n",
      "2024-06-03, Close, 3078.49\n",
      "2024-06-03, SELL CREATE, 3078.49\n",
      "2024-06-04, SELL EXECUTED, Price: 3071.32, Cost: 3094.14, Comm 3.07\n",
      "2024-06-04, OPERATION PROFIT, GROSS -22.82, NET -28.99\n",
      "2024-06-04, Close, 3091.20\n",
      "2024-06-05, Close, 3065.40\n",
      "2024-06-06, Close, 3048.79\n",
      "2024-06-06, BUY CREATE, 3048.79\n",
      "2024-06-07, BUY EXECUTED, Price: 3053.92, Cost: 3053.92, Comm 3.05\n",
      "2024-06-07, Close, 3051.28\n",
      "2024-06-11, Close, 3028.04\n",
      "2024-06-12, Close, 3037.47\n",
      "2024-06-13, Close, 3028.92\n",
      "2024-06-14, Close, 3032.63\n",
      "2024-06-17, Close, 3015.89\n",
      "2024-06-17, SELL CREATE, 3015.89\n",
      "Final Portfolio Value: 100115.06\n"
     ]
    }
   ],
   "source": [
    "from __future__ import (absolute_import, division, print_function,\n",
    "                        unicode_literals)\n",
    "\n",
    "import datetime  # For datetime objects\n",
    "import os.path  # To manage paths\n",
    "import sys  # To find out the script name (in argv[0])\n",
    "\n",
    "# Import the backtrader platform\n",
    "import backtrader as bt\n",
    "\n",
    "\n",
    "# Create a Stratey\n",
    "class TestStrategy(bt.Strategy):\n",
    "\n",
    "    def log(self, txt, dt=None):\n",
    "        ''' Logging function fot this strategy'''\n",
    "        dt = dt or self.datas[0].datetime.date(0)\n",
    "        print('%s, %s' % (dt.isoformat(), txt))\n",
    "\n",
    "    def __init__(self):\n",
    "        # Keep a reference to the \"close\" line in the data[0] dataseries\n",
    "        self.dataclose = self.datas[0].close\n",
    "\n",
    "        # To keep track of pending orders and buy price/commission\n",
    "        self.order = None\n",
    "        self.buyprice = None\n",
    "        self.buycomm = None\n",
    "\n",
    "    def notify_order(self, order):\n",
    "        if order.status in [order.Submitted, order.Accepted]:\n",
    "            # Buy/Sell order submitted/accepted to/by broker - Nothing to do\n",
    "            return\n",
    "\n",
    "        # Check if an order has been completed\n",
    "        # Attention: broker could reject order if not enough cash\n",
    "        if order.status in [order.Completed]:\n",
    "            if order.isbuy():\n",
    "                self.log(\n",
    "                    'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %\n",
    "                    (order.executed.price,\n",
    "                     order.executed.value,\n",
    "                     order.executed.comm))\n",
    "\n",
    "                self.buyprice = order.executed.price\n",
    "                self.buycomm = order.executed.comm\n",
    "            else:  # Sell\n",
    "                self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %\n",
    "                         (order.executed.price,\n",
    "                          order.executed.value,\n",
    "                          order.executed.comm))\n",
    "            # 将当前的柱线数量（即 len(self)）记录在 self.bar_executed 中，以便后续使用。\n",
    "            self.bar_executed = len(self)\n",
    "\n",
    "        elif order.status in [order.Canceled, order.Margin, order.Rejected]:\n",
    "            self.log('Order Canceled/Margin/Rejected')\n",
    "        # 无论订单的状态如何，都将 self.order 设置为 None，表示当前没有未决订单。\n",
    "        self.order = None\n",
    "\n",
    "    def notify_trade(self, trade):# 处理交易状态的通知。\n",
    "        if not trade.isclosed:  # 如果交易尚未关闭，则方法直接返回，不执行任何进一步的操作。\n",
    "            return\n",
    "\n",
    "        self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %\n",
    "                 (trade.pnl, trade.pnlcomm))\n",
    "        '''\n",
    "        交易框架中定义好的一个属性。trade 对象可能会有以下属性：\n",
    "            trade.price：交易的买入或卖出价格。\n",
    "            trade.size：交易的量（如股票数量）。\n",
    "            trade.commission：交易的佣金。\n",
    "            trade.pnl：交易的毛利润。pnl 是 \"Profit and Loss\" 的缩写\n",
    "            trade.pnlcomm：扣除佣金后的净利润。\n",
    "        '''\n",
    "\n",
    "    def next(self):\n",
    "        # Simply log the closing price of the series from the reference\n",
    "        self.log('Close, %.2f' % self.dataclose[0])\n",
    "\n",
    "        # Check if an order is pending ... if yes, we cannot send a 2nd one\n",
    "        if self.order:\n",
    "            return\n",
    "\n",
    "        # Check if we are in the market\n",
    "        if not self.position:\n",
    "\n",
    "            # Not yet ... we MIGHT BUY if ...\n",
    "            if self.dataclose[0] < self.dataclose[-1]:\n",
    "                    # current close less than previous close\n",
    "\n",
    "                    if self.dataclose[-1] < self.dataclose[-2]:\n",
    "                        # previous close less than the previous close\n",
    "\n",
    "                        # BUY, BUY, BUY!!! (with default parameters)\n",
    "                        self.log('BUY CREATE, %.2f' % self.dataclose[0])\n",
    "\n",
    "                        # Keep track of the created order to avoid a 2nd order\n",
    "                        self.order = self.buy()\n",
    "\n",
    "        else:\n",
    "\n",
    "            # Already in the market ... we might sell\n",
    "            if len(self) >= (self.bar_executed + 5):\n",
    "                # SELL, SELL, SELL!!! (with all possible default parameters)\n",
    "                self.log('SELL CREATE, %.2f' % self.dataclose[0])\n",
    "\n",
    "                # Keep track of the created order to avoid a 2nd order\n",
    "                self.order = self.sell()\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    # Create a cerebro entity\n",
    "    cerebro = bt.Cerebro()\n",
    "\n",
    "    # Add a strategy\n",
    "    cerebro.addstrategy(TestStrategy)\n",
    "\n",
    "    # Datas are in a subfolder of the samples. Need to find where the script is\n",
    "    # because it could have been called from anywhere\n",
    "    # modpath = os.path.dirname(os.path.abspath(sys.argv[0]))\n",
    "    modpath = 'C:\\\\Users\\\\bww\\gitee\\\\stock\\\\backtrader'\n",
    "    datapath = os.path.join(modpath, '000001.SS.csv')\n",
    "\n",
    "    # Create a Data Feed\n",
    "    data = bt.feeds.YahooFinanceCSVData(\n",
    "        dataname=datapath,\n",
    "        # Do not pass values before this date\n",
    "        fromdate=datetime.datetime(2000, 1, 1),\n",
    "        # Do not pass values before this date\n",
    "        todate=datetime.datetime(2024, 12, 31),\n",
    "        # Do not pass values after this date\n",
    "        reverse=False)\n",
    "\n",
    "    # Add the Data Feed to Cerebro\n",
    "    cerebro.adddata(data)\n",
    "\n",
    "    # Set our desired cash start\n",
    "    cerebro.broker.setcash(100000.0)\n",
    "\n",
    "    # Set the commission - 0.1% ... divide by 100 to remove the %\n",
    "    cerebro.broker.setcommission(commission=0.001)\n",
    "\n",
    "    # Print out the starting conditions\n",
    "    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())\n",
    "\n",
    "    # Run over everything\n",
    "    cerebro.run()\n",
    "\n",
    "    # Print out the final result\n",
    "    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Customizing the Strategy: Parameters\n",
    "```\n",
    "    params = (\n",
    "        ('exitbars', 5),\n",
    "    )\n",
    "```\n",
    "self.params.exitbars\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Starting Portfolio Value: 100000.00\n",
      "2023-06-19, Close, 3255.81\n",
      "2023-06-20, Close, 3240.36\n",
      "2023-06-21, Close, 3197.90\n",
      "2023-06-21, BUY CREATE, 3197.90\n",
      "2023-06-26, BUY EXECUTED, Price: 3177.23, Cost: 31772.30, Comm 31.77\n",
      "2023-06-26, Close, 3150.62\n",
      "2023-06-27, Close, 3189.44\n",
      "2023-06-28, Close, 3189.38\n",
      "2023-06-29, Close, 3182.38\n",
      "2023-06-30, Close, 3202.06\n",
      "2023-07-03, Close, 3243.98\n",
      "2023-07-03, SELL CREATE, 3243.98\n",
      "2023-07-04, SELL EXECUTED, Price: 3241.23, Cost: 31772.30, Comm 32.41\n",
      "2023-07-04, OPERATION PROFIT, GROSS 640.00, NET 575.82\n",
      "2023-07-04, Close, 3245.35\n",
      "2023-07-05, Close, 3222.95\n",
      "2023-07-06, Close, 3205.57\n",
      "2023-07-06, BUY CREATE, 3205.57\n",
      "2023-07-07, BUY EXECUTED, Price: 3197.47, Cost: 31974.70, Comm 31.97\n",
      "2023-07-07, Close, 3196.61\n",
      "2023-07-10, Close, 3203.70\n",
      "2023-07-11, Close, 3221.37\n",
      "2023-07-12, Close, 3196.13\n",
      "2023-07-13, Close, 3236.48\n",
      "2023-07-14, Close, 3237.70\n",
      "2023-07-14, SELL CREATE, 3237.70\n",
      "2023-07-17, SELL EXECUTED, Price: 3219.02, Cost: 31974.70, Comm 32.19\n",
      "2023-07-17, OPERATION PROFIT, GROSS 215.50, NET 151.34\n",
      "2023-07-17, Close, 3209.63\n",
      "2023-07-18, Close, 3197.82\n",
      "2023-07-18, BUY CREATE, 3197.82\n",
      "2023-07-19, BUY EXECUTED, Price: 3195.01, Cost: 31950.10, Comm 31.95\n",
      "2023-07-19, Close, 3198.84\n",
      "2023-07-20, Close, 3169.52\n",
      "2023-07-21, Close, 3167.75\n",
      "2023-07-24, Close, 3164.16\n",
      "2023-07-25, Close, 3231.52\n",
      "2023-07-26, Close, 3223.03\n",
      "2023-07-26, SELL CREATE, 3223.03\n",
      "2023-07-27, SELL EXECUTED, Price: 3225.48, Cost: 31950.10, Comm 32.25\n",
      "2023-07-27, OPERATION PROFIT, GROSS 304.70, NET 240.50\n",
      "2023-07-27, Close, 3216.67\n",
      "2023-07-27, BUY CREATE, 3216.67\n",
      "2023-07-28, BUY EXECUTED, Price: 3206.74, Cost: 32067.40, Comm 32.07\n",
      "2023-07-28, Close, 3275.93\n",
      "2023-07-31, Close, 3291.04\n",
      "2023-08-01, Close, 3290.95\n",
      "2023-08-02, Close, 3261.69\n",
      "2023-08-03, Close, 3280.46\n",
      "2023-08-04, Close, 3288.08\n",
      "2023-08-04, SELL CREATE, 3288.08\n",
      "2023-08-07, SELL EXECUTED, Price: 3276.78, Cost: 32067.40, Comm 32.77\n",
      "2023-08-07, OPERATION PROFIT, GROSS 700.40, NET 635.56\n",
      "2023-08-07, Close, 3268.83\n",
      "2023-08-08, Close, 3260.62\n",
      "2023-08-08, BUY CREATE, 3260.62\n",
      "2023-08-09, BUY EXECUTED, Price: 3251.15, Cost: 32511.50, Comm 32.51\n",
      "2023-08-09, Close, 3244.49\n",
      "2023-08-10, Close, 3254.56\n",
      "2023-08-11, Close, 3189.25\n",
      "2023-08-14, Close, 3178.43\n",
      "2023-08-15, Close, 3176.18\n",
      "2023-08-16, Close, 3150.13\n",
      "2023-08-16, SELL CREATE, 3150.13\n",
      "2023-08-17, SELL EXECUTED, Price: 3137.35, Cost: 32511.50, Comm 31.37\n",
      "2023-08-17, OPERATION PROFIT, GROSS -1138.00, NET -1201.89\n",
      "2023-08-17, Close, 3163.74\n",
      "2023-08-18, Close, 3131.95\n",
      "2023-08-21, Close, 3092.98\n",
      "2023-08-21, BUY CREATE, 3092.98\n",
      "2023-08-22, BUY EXECUTED, Price: 3103.78, Cost: 31037.80, Comm 31.04\n",
      "2023-08-22, Close, 3120.33\n",
      "2023-08-23, Close, 3078.40\n",
      "2023-08-24, Close, 3082.24\n",
      "2023-08-25, Close, 3064.07\n",
      "2023-08-28, Close, 3098.64\n",
      "2023-08-29, Close, 3135.89\n",
      "2023-08-29, SELL CREATE, 3135.89\n",
      "2023-08-30, SELL EXECUTED, Price: 3144.41, Cost: 31037.80, Comm 31.44\n",
      "2023-08-30, OPERATION PROFIT, GROSS 406.30, NET 343.82\n",
      "2023-08-30, Close, 3137.14\n",
      "2023-08-31, Close, 3119.88\n",
      "2023-09-01, Close, 3133.25\n",
      "2023-09-04, Close, 3177.06\n",
      "2023-09-05, Close, 3154.37\n",
      "2023-09-06, Close, 3158.08\n",
      "2023-09-07, Close, 3122.35\n",
      "2023-09-08, Close, 3116.72\n",
      "2023-09-08, BUY CREATE, 3116.72\n",
      "2023-09-11, BUY EXECUTED, Price: 3119.61, Cost: 31196.10, Comm 31.20\n",
      "2023-09-11, Close, 3142.78\n",
      "2023-09-12, Close, 3137.06\n",
      "2023-09-13, Close, 3123.07\n",
      "2023-09-14, Close, 3126.55\n",
      "2023-09-15, Close, 3117.74\n",
      "2023-09-18, Close, 3125.93\n",
      "2023-09-18, SELL CREATE, 3125.93\n",
      "2023-09-19, SELL EXECUTED, Price: 3123.99, Cost: 31196.10, Comm 31.24\n",
      "2023-09-19, OPERATION PROFIT, GROSS 43.80, NET -18.64\n",
      "2023-09-19, Close, 3124.96\n",
      "2023-09-20, Close, 3108.57\n",
      "2023-09-20, BUY CREATE, 3108.57\n",
      "2023-09-21, BUY EXECUTED, Price: 3103.18, Cost: 31031.80, Comm 31.03\n",
      "2023-09-21, Close, 3084.70\n",
      "2023-09-22, Close, 3132.43\n",
      "2023-09-25, Close, 3115.61\n",
      "2023-09-26, Close, 3102.27\n",
      "2023-09-27, Close, 3107.32\n",
      "2023-09-28, Close, 3110.48\n",
      "2023-09-28, SELL CREATE, 3110.48\n",
      "2023-10-09, SELL EXECUTED, Price: 3100.00, Cost: 31031.80, Comm 31.00\n",
      "2023-10-09, OPERATION PROFIT, GROSS -31.80, NET -93.83\n",
      "2023-10-09, Close, 3096.92\n",
      "2023-10-10, Close, 3075.24\n",
      "2023-10-10, BUY CREATE, 3075.24\n",
      "2023-10-11, BUY EXECUTED, Price: 3085.70, Cost: 30857.00, Comm 30.86\n",
      "2023-10-11, Close, 3078.96\n",
      "2023-10-12, Close, 3107.90\n",
      "2023-10-13, Close, 3088.10\n",
      "2023-10-16, Close, 3073.81\n",
      "2023-10-17, Close, 3083.50\n",
      "2023-10-18, Close, 3058.71\n",
      "2023-10-18, SELL CREATE, 3058.71\n",
      "2023-10-19, SELL EXECUTED, Price: 3043.20, Cost: 30857.00, Comm 30.43\n",
      "2023-10-19, OPERATION PROFIT, GROSS -425.00, NET -486.29\n",
      "2023-10-19, Close, 3005.39\n",
      "2023-10-19, BUY CREATE, 3005.39\n",
      "2023-10-20, BUY EXECUTED, Price: 2995.36, Cost: 29953.60, Comm 29.95\n",
      "2023-10-20, Close, 2983.06\n",
      "2023-10-23, Close, 2939.29\n",
      "2023-10-24, Close, 2962.24\n",
      "2023-10-25, Close, 2974.11\n",
      "2023-10-26, Close, 2988.30\n",
      "2023-10-27, Close, 3017.78\n",
      "2023-10-27, SELL CREATE, 3017.78\n",
      "2023-10-30, SELL EXECUTED, Price: 3009.61, Cost: 29953.60, Comm 30.10\n",
      "2023-10-30, OPERATION PROFIT, GROSS 142.50, NET 82.45\n",
      "2023-10-30, Close, 3021.55\n",
      "2023-10-31, Close, 3018.77\n",
      "2023-11-01, Close, 3023.08\n",
      "2023-11-02, Close, 3009.41\n",
      "2023-11-03, Close, 3030.80\n",
      "2023-11-06, Close, 3058.41\n",
      "2023-11-07, Close, 3057.27\n",
      "2023-11-08, Close, 3052.37\n",
      "2023-11-08, BUY CREATE, 3052.37\n",
      "2023-11-09, BUY EXECUTED, Price: 3047.65, Cost: 30476.50, Comm 30.48\n",
      "2023-11-09, Close, 3053.28\n",
      "2023-11-10, Close, 3038.97\n",
      "2023-11-13, Close, 3046.53\n",
      "2023-11-14, Close, 3056.07\n",
      "2023-11-15, Close, 3072.83\n",
      "2023-11-16, Close, 3050.93\n",
      "2023-11-16, SELL CREATE, 3050.93\n",
      "2023-11-17, SELL EXECUTED, Price: 3043.62, Cost: 30476.50, Comm 30.44\n",
      "2023-11-17, OPERATION PROFIT, GROSS -40.30, NET -101.21\n",
      "2023-11-17, Close, 3054.37\n",
      "2023-11-20, Close, 3068.32\n",
      "2023-11-21, Close, 3067.93\n",
      "2023-11-22, Close, 3043.61\n",
      "2023-11-22, BUY CREATE, 3043.61\n",
      "2023-11-23, BUY EXECUTED, Price: 3041.68, Cost: 30416.80, Comm 30.42\n",
      "2023-11-23, Close, 3061.86\n",
      "2023-11-24, Close, 3040.97\n",
      "2023-11-27, Close, 3031.70\n",
      "2023-11-28, Close, 3038.55\n",
      "2023-11-29, Close, 3021.69\n",
      "2023-11-30, Close, 3029.67\n",
      "2023-11-30, SELL CREATE, 3029.67\n",
      "2023-12-01, SELL EXECUTED, Price: 3027.35, Cost: 30416.80, Comm 30.27\n",
      "2023-12-01, OPERATION PROFIT, GROSS -143.30, NET -203.99\n",
      "2023-12-01, Close, 3031.64\n",
      "2023-12-04, Close, 3022.91\n",
      "2023-12-05, Close, 2972.30\n",
      "2023-12-05, BUY CREATE, 2972.30\n",
      "2023-12-06, BUY EXECUTED, Price: 2966.95, Cost: 29669.50, Comm 29.67\n",
      "2023-12-06, Close, 2968.93\n",
      "2023-12-07, Close, 2966.21\n",
      "2023-12-08, Close, 2969.56\n",
      "2023-12-11, Close, 2991.44\n",
      "2023-12-12, Close, 3003.44\n",
      "2023-12-13, Close, 2968.76\n",
      "2023-12-13, SELL CREATE, 2968.76\n",
      "2023-12-14, SELL EXECUTED, Price: 2979.70, Cost: 29669.50, Comm 29.80\n",
      "2023-12-14, OPERATION PROFIT, GROSS 127.50, NET 68.03\n",
      "2023-12-14, Close, 2958.99\n",
      "2023-12-14, BUY CREATE, 2958.99\n",
      "2023-12-15, BUY EXECUTED, Price: 2966.30, Cost: 29663.00, Comm 29.66\n",
      "2023-12-15, Close, 2942.56\n",
      "2023-12-18, Close, 2930.80\n",
      "2023-12-19, Close, 2932.39\n",
      "2023-12-20, Close, 2902.11\n",
      "2023-12-21, Close, 2918.72\n",
      "2023-12-22, Close, 2914.77\n",
      "2023-12-22, SELL CREATE, 2914.77\n",
      "2023-12-25, SELL EXECUTED, Price: 2909.88, Cost: 29663.00, Comm 29.10\n",
      "2023-12-25, OPERATION PROFIT, GROSS -564.20, NET -622.96\n",
      "2023-12-25, Close, 2918.81\n",
      "2023-12-26, Close, 2898.88\n",
      "2023-12-27, Close, 2914.61\n",
      "2023-12-28, Close, 2954.70\n",
      "2023-12-29, Close, 2974.94\n",
      "2024-01-02, Close, 2962.28\n",
      "2024-01-03, Close, 2967.25\n",
      "2024-01-04, Close, 2954.35\n",
      "2024-01-05, Close, 2929.18\n",
      "2024-01-05, BUY CREATE, 2929.18\n",
      "2024-01-08, BUY EXECUTED, Price: 2923.46, Cost: 29234.60, Comm 29.23\n",
      "2024-01-08, Close, 2887.54\n",
      "2024-01-09, Close, 2893.25\n",
      "2024-01-10, Close, 2877.70\n",
      "2024-01-11, Close, 2886.65\n",
      "2024-01-12, Close, 2881.98\n",
      "2024-01-15, Close, 2886.29\n",
      "2024-01-15, SELL CREATE, 2886.29\n",
      "2024-01-16, SELL EXECUTED, Price: 2881.56, Cost: 29234.60, Comm 28.82\n",
      "2024-01-16, OPERATION PROFIT, GROSS -419.00, NET -477.05\n",
      "2024-01-16, Close, 2893.99\n",
      "2024-01-17, Close, 2833.61\n",
      "2024-01-18, Close, 2845.78\n",
      "2024-01-19, Close, 2832.28\n",
      "2024-01-22, Close, 2756.34\n",
      "2024-01-22, BUY CREATE, 2756.34\n",
      "2024-01-23, BUY EXECUTED, Price: 2746.89, Cost: 27468.90, Comm 27.47\n",
      "2024-01-23, Close, 2770.98\n",
      "2024-01-24, Close, 2820.77\n",
      "2024-01-25, Close, 2906.11\n",
      "2024-01-26, Close, 2910.22\n",
      "2024-01-29, Close, 2883.36\n",
      "2024-01-30, Close, 2830.53\n",
      "2024-01-30, SELL CREATE, 2830.53\n",
      "2024-01-31, SELL EXECUTED, Price: 2815.50, Cost: 27468.90, Comm 28.16\n",
      "2024-01-31, OPERATION PROFIT, GROSS 686.10, NET 630.48\n",
      "2024-01-31, Close, 2788.55\n",
      "2024-01-31, BUY CREATE, 2788.55\n",
      "2024-02-01, BUY EXECUTED, Price: 2773.42, Cost: 27734.20, Comm 27.73\n",
      "2024-02-01, Close, 2770.74\n",
      "2024-02-02, Close, 2730.15\n",
      "2024-02-05, Close, 2702.19\n",
      "2024-02-06, Close, 2789.49\n",
      "2024-02-07, Close, 2829.70\n",
      "2024-02-08, Close, 2865.90\n",
      "2024-02-08, SELL CREATE, 2865.90\n",
      "2024-02-19, SELL EXECUTED, Price: 2886.59, Cost: 27734.20, Comm 28.87\n",
      "2024-02-19, OPERATION PROFIT, GROSS 1131.70, NET 1075.10\n",
      "2024-02-19, Close, 2910.54\n",
      "2024-02-20, Close, 2922.73\n",
      "2024-02-21, Close, 2950.96\n",
      "2024-02-22, Close, 2988.36\n",
      "2024-02-23, Close, 3004.88\n",
      "2024-02-26, Close, 2977.02\n",
      "2024-02-27, Close, 3015.48\n",
      "2024-02-28, Close, 2957.85\n",
      "2024-02-29, Close, 3015.17\n",
      "2024-03-01, Close, 3027.02\n",
      "2024-03-04, Close, 3039.31\n",
      "2024-03-05, Close, 3047.79\n",
      "2024-03-06, Close, 3039.93\n",
      "2024-03-07, Close, 3027.40\n",
      "2024-03-07, BUY CREATE, 3027.40\n",
      "2024-03-08, BUY EXECUTED, Price: 3026.17, Cost: 30261.70, Comm 30.26\n",
      "2024-03-08, Close, 3046.02\n",
      "2024-03-11, Close, 3068.46\n",
      "2024-03-12, Close, 3055.94\n",
      "2024-03-13, Close, 3043.83\n",
      "2024-03-14, Close, 3038.23\n",
      "2024-03-15, Close, 3054.64\n",
      "2024-03-15, SELL CREATE, 3054.64\n",
      "2024-03-18, SELL EXECUTED, Price: 3057.21, Cost: 30261.70, Comm 30.57\n",
      "2024-03-18, OPERATION PROFIT, GROSS 310.40, NET 249.57\n",
      "2024-03-18, Close, 3084.93\n",
      "2024-03-19, Close, 3062.76\n",
      "2024-03-20, Close, 3079.69\n",
      "2024-03-21, Close, 3077.11\n",
      "2024-03-22, Close, 3048.03\n",
      "2024-03-22, BUY CREATE, 3048.03\n",
      "2024-03-25, BUY EXECUTED, Price: 3040.50, Cost: 30405.00, Comm 30.41\n",
      "2024-03-25, Close, 3026.31\n",
      "2024-03-26, Close, 3031.48\n",
      "2024-03-27, Close, 2993.14\n",
      "2024-03-28, Close, 3010.66\n",
      "2024-03-29, Close, 3041.17\n",
      "2024-04-01, Close, 3077.38\n",
      "2024-04-01, SELL CREATE, 3077.38\n",
      "2024-04-02, SELL EXECUTED, Price: 3076.88, Cost: 30405.00, Comm 30.77\n",
      "2024-04-02, OPERATION PROFIT, GROSS 363.80, NET 302.63\n",
      "2024-04-02, Close, 3074.96\n",
      "2024-04-03, Close, 3069.30\n",
      "2024-04-03, BUY CREATE, 3069.30\n",
      "2024-04-08, BUY EXECUTED, Price: 3057.33, Cost: 30573.30, Comm 30.57\n",
      "2024-04-08, Close, 3047.05\n",
      "2024-04-09, Close, 3048.54\n",
      "2024-04-10, Close, 3027.33\n",
      "2024-04-11, Close, 3034.25\n",
      "2024-04-12, Close, 3019.47\n",
      "2024-04-15, Close, 3057.38\n",
      "2024-04-15, SELL CREATE, 3057.38\n",
      "2024-04-16, SELL EXECUTED, Price: 3043.55, Cost: 30573.30, Comm 30.44\n",
      "2024-04-16, OPERATION PROFIT, GROSS -137.80, NET -198.81\n",
      "2024-04-16, Close, 3007.07\n",
      "2024-04-17, Close, 3071.38\n",
      "2024-04-18, Close, 3074.23\n",
      "2024-04-19, Close, 3065.26\n",
      "2024-04-22, Close, 3044.59\n",
      "2024-04-22, BUY CREATE, 3044.59\n",
      "2024-04-23, BUY EXECUTED, Price: 3038.61, Cost: 30386.10, Comm 30.39\n",
      "2024-04-23, Close, 3021.98\n",
      "2024-04-24, Close, 3044.82\n",
      "2024-04-25, Close, 3052.90\n",
      "2024-04-26, Close, 3088.64\n",
      "2024-04-29, Close, 3113.04\n",
      "2024-04-30, Close, 3104.82\n",
      "2024-04-30, SELL CREATE, 3104.82\n",
      "2024-05-06, SELL EXECUTED, Price: 3132.51, Cost: 30386.10, Comm 31.33\n",
      "2024-05-06, OPERATION PROFIT, GROSS 939.00, NET 877.29\n",
      "2024-05-06, Close, 3140.72\n",
      "2024-05-07, Close, 3147.74\n",
      "2024-05-08, Close, 3128.48\n",
      "2024-05-09, Close, 3154.32\n",
      "2024-05-10, Close, 3154.55\n",
      "2024-05-13, Close, 3148.02\n",
      "2024-05-14, Close, 3145.77\n",
      "2024-05-14, BUY CREATE, 3145.77\n",
      "2024-05-15, BUY EXECUTED, Price: 3141.25, Cost: 31412.50, Comm 31.41\n",
      "2024-05-15, Close, 3119.90\n",
      "2024-05-16, Close, 3122.40\n",
      "2024-05-17, Close, 3154.03\n",
      "2024-05-20, Close, 3171.15\n",
      "2024-05-21, Close, 3157.97\n",
      "2024-05-22, Close, 3158.54\n",
      "2024-05-22, SELL CREATE, 3158.54\n",
      "2024-05-23, SELL EXECUTED, Price: 3149.88, Cost: 31412.50, Comm 31.50\n",
      "2024-05-23, OPERATION PROFIT, GROSS 86.30, NET 23.39\n",
      "2024-05-23, Close, 3116.39\n",
      "2024-05-24, Close, 3088.87\n",
      "2024-05-24, BUY CREATE, 3088.87\n",
      "2024-05-27, BUY EXECUTED, Price: 3094.14, Cost: 30941.40, Comm 30.94\n",
      "2024-05-27, Close, 3124.04\n",
      "2024-05-28, Close, 3109.57\n",
      "2024-05-29, Close, 3111.02\n",
      "2024-05-30, Close, 3091.68\n",
      "2024-05-31, Close, 3086.81\n",
      "2024-06-03, Close, 3078.49\n",
      "2024-06-03, SELL CREATE, 3078.49\n",
      "2024-06-04, SELL EXECUTED, Price: 3071.32, Cost: 30941.40, Comm 30.71\n",
      "2024-06-04, OPERATION PROFIT, GROSS -228.20, NET -289.85\n",
      "2024-06-04, Close, 3091.20\n",
      "2024-06-05, Close, 3065.40\n",
      "2024-06-06, Close, 3048.79\n",
      "2024-06-06, BUY CREATE, 3048.79\n",
      "2024-06-07, BUY EXECUTED, Price: 3053.92, Cost: 30539.20, Comm 30.54\n",
      "2024-06-07, Close, 3051.28\n",
      "2024-06-11, Close, 3028.04\n",
      "2024-06-12, Close, 3037.47\n",
      "2024-06-13, Close, 3028.92\n",
      "2024-06-14, Close, 3032.63\n",
      "2024-06-17, Close, 3015.89\n",
      "2024-06-17, SELL CREATE, 3015.89\n",
      "Final Portfolio Value: 101150.60\n"
     ]
    }
   ],
   "source": [
    "from __future__ import (absolute_import, division, print_function,\n",
    "                        unicode_literals)\n",
    "\n",
    "import datetime  # For datetime objects\n",
    "import os.path  # To manage paths\n",
    "import sys  # To find out the script name (in argv[0])\n",
    "\n",
    "# Import the backtrader platform\n",
    "import backtrader as bt\n",
    "\n",
    "\n",
    "# Create a Stratey\n",
    "class TestStrategy(bt.Strategy):\n",
    "    params = (\n",
    "        ('exitbars', 5),\n",
    "    )\n",
    "\n",
    "    def log(self, txt, dt=None):\n",
    "        ''' Logging function fot this strategy'''\n",
    "        dt = dt or self.datas[0].datetime.date(0)\n",
    "        print('%s, %s' % (dt.isoformat(), txt))\n",
    "\n",
    "    def __init__(self):\n",
    "        # Keep a reference to the \"close\" line in the data[0] dataseries\n",
    "        self.dataclose = self.datas[0].close\n",
    "\n",
    "        # To keep track of pending orders and buy price/commission\n",
    "        self.order = None\n",
    "        self.buyprice = None\n",
    "        self.buycomm = None\n",
    "\n",
    "    def notify_order(self, order):\n",
    "        if order.status in [order.Submitted, order.Accepted]:\n",
    "            # Buy/Sell order submitted/accepted to/by broker - Nothing to do\n",
    "            return\n",
    "\n",
    "        # Check if an order has been completed\n",
    "        # Attention: broker could reject order if not enough cash\n",
    "        if order.status in [order.Completed]:\n",
    "            if order.isbuy():\n",
    "                self.log(\n",
    "                    'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %\n",
    "                    (order.executed.price,\n",
    "                     order.executed.value,\n",
    "                     order.executed.comm))\n",
    "\n",
    "                self.buyprice = order.executed.price\n",
    "                self.buycomm = order.executed.comm\n",
    "            else:  # Sell\n",
    "                self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %\n",
    "                         (order.executed.price,\n",
    "                          order.executed.value,\n",
    "                          order.executed.comm))\n",
    "\n",
    "            self.bar_executed = len(self)\n",
    "\n",
    "        elif order.status in [order.Canceled, order.Margin, order.Rejected]:\n",
    "            self.log('Order Canceled/Margin/Rejected')\n",
    "\n",
    "        self.order = None\n",
    "\n",
    "    def notify_trade(self, trade):\n",
    "        if not trade.isclosed:\n",
    "            return\n",
    "\n",
    "        self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %\n",
    "                 (trade.pnl, trade.pnlcomm))\n",
    "\n",
    "    def next(self):\n",
    "        # Simply log the closing price of the series from the reference\n",
    "        self.log('Close, %.2f' % self.dataclose[0])\n",
    "\n",
    "        # Check if an order is pending ... if yes, we cannot send a 2nd one\n",
    "        if self.order:\n",
    "            return\n",
    "\n",
    "        # Check if we are in the market\n",
    "        if not self.position:\n",
    "\n",
    "            # Not yet ... we MIGHT BUY if ...\n",
    "            if self.dataclose[0] < self.dataclose[-1]:\n",
    "                    # current close less than previous close\n",
    "\n",
    "                    if self.dataclose[-1] < self.dataclose[-2]:\n",
    "                        # previous close less than the previous close\n",
    "\n",
    "                        # BUY, BUY, BUY!!! (with default parameters)\n",
    "                        self.log('BUY CREATE, %.2f' % self.dataclose[0])\n",
    "\n",
    "                        # Keep track of the created order to avoid a 2nd order\n",
    "                        self.order = self.buy()\n",
    "\n",
    "        else:\n",
    "\n",
    "            # Already in the market ... we might sell\n",
    "            if len(self) >= (self.bar_executed + self.params.exitbars):\n",
    "                # SELL, SELL, SELL!!! (with all possible default parameters)\n",
    "                self.log('SELL CREATE, %.2f' % self.dataclose[0])\n",
    "\n",
    "                # Keep track of the created order to avoid a 2nd order\n",
    "                self.order = self.sell()\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    # Create a cerebro entity\n",
    "    cerebro = bt.Cerebro()\n",
    "\n",
    "    # Add a strategy\n",
    "    cerebro.addstrategy(TestStrategy)\n",
    "\n",
    "    # Datas are in a subfolder of the samples. Need to find where the script is\n",
    "    # because it could have been called from anywhere\n",
    "    # modpath = os.path.dirname(os.path.abspath(sys.argv[0]))\n",
    "    modpath = 'C:\\\\Users\\\\bww\\gitee\\\\stock\\\\backtrader'\n",
    "    datapath = os.path.join(modpath, '000001.SS.csv')\n",
    "\n",
    "    # Create a Data Feed\n",
    "    data = bt.feeds.YahooFinanceCSVData(\n",
    "        dataname=datapath,\n",
    "        # Do not pass values before this date\n",
    "        fromdate=datetime.datetime(2000, 1, 1),\n",
    "        # Do not pass values before this date\n",
    "        todate=datetime.datetime(2024, 12, 31),\n",
    "        # Do not pass values after this date\n",
    "        reverse=False)\n",
    "\n",
    "    # Add the Data Feed to Cerebro\n",
    "    cerebro.adddata(data)\n",
    "\n",
    "    # Set our desired cash start\n",
    "    cerebro.broker.setcash(100000.0)\n",
    "\n",
    "    # Add a FixedSize sizer according to the stake\n",
    "    cerebro.addsizer(bt.sizers.FixedSize, stake=10)\n",
    "\n",
    "    # Set the commission - 0.1% ... divide by 100 to remove the %\n",
    "    cerebro.broker.setcommission(commission=0.001)\n",
    "\n",
    "    # Print out the starting conditions\n",
    "    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())\n",
    "\n",
    "    # Run over everything\n",
    "    cerebro.run()\n",
    "\n",
    "    # Print out the final result\n",
    "    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 添加指标\n",
    "如果收盘价大于平均值，以市价“买入”\n",
    "\n",
    "如果处于市场中，则在收盘价小于平均值时“卖出”\n",
    "\n",
    "```self.sma = bt.indicators.MovingAverageSimple(self.datas[0], period=self.params.maperiod)```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Starting Portfolio Value: 100000.00\n",
      "2023-07-11, Close, 3221.37\n",
      "2023-07-11, BUY CREATE, 3221.37\n",
      "2023-07-12, BUY EXECUTED, Price: 3220.38, Cost: 32203.80, Comm 0.00\n",
      "2023-07-12, Close, 3196.13\n",
      "2023-07-12, SELL CREATE, 3196.13\n",
      "2023-07-13, SELL EXECUTED, Price: 3202.70, Cost: 32203.80, Comm 0.00\n",
      "2023-07-13, OPERATION PROFIT, GROSS -176.80, NET -176.80\n",
      "2023-07-13, Close, 3236.48\n",
      "2023-07-13, BUY CREATE, 3236.48\n",
      "2023-07-14, BUY EXECUTED, Price: 3240.97, Cost: 32409.70, Comm 0.00\n",
      "2023-07-14, Close, 3237.70\n",
      "2023-07-17, Close, 3209.63\n",
      "2023-07-17, SELL CREATE, 3209.63\n",
      "2023-07-18, SELL EXECUTED, Price: 3206.79, Cost: 32409.70, Comm 0.00\n",
      "2023-07-18, OPERATION PROFIT, GROSS -341.80, NET -341.80\n",
      "2023-07-18, Close, 3197.82\n",
      "2023-07-19, Close, 3198.84\n",
      "2023-07-20, Close, 3169.52\n",
      "2023-07-21, Close, 3167.75\n",
      "2023-07-24, Close, 3164.16\n",
      "2023-07-25, Close, 3231.52\n",
      "2023-07-25, BUY CREATE, 3231.52\n",
      "2023-07-26, BUY EXECUTED, Price: 3228.09, Cost: 32280.90, Comm 0.00\n",
      "2023-07-26, Close, 3223.03\n",
      "2023-07-27, Close, 3216.67\n",
      "2023-07-28, Close, 3275.93\n",
      "2023-07-31, Close, 3291.04\n",
      "2023-08-01, Close, 3290.95\n",
      "2023-08-02, Close, 3261.69\n",
      "2023-08-03, Close, 3280.46\n",
      "2023-08-04, Close, 3288.08\n",
      "2023-08-07, Close, 3268.83\n",
      "2023-08-08, Close, 3260.62\n",
      "2023-08-09, Close, 3244.49\n",
      "2023-08-10, Close, 3254.56\n",
      "2023-08-11, Close, 3189.25\n",
      "2023-08-11, SELL CREATE, 3189.25\n",
      "2023-08-14, SELL EXECUTED, Price: 3159.71, Cost: 32280.90, Comm 0.00\n",
      "2023-08-14, OPERATION PROFIT, GROSS -683.80, NET -683.80\n",
      "2023-08-14, Close, 3178.43\n",
      "2023-08-15, Close, 3176.18\n",
      "2023-08-16, Close, 3150.13\n",
      "2023-08-17, Close, 3163.74\n",
      "2023-08-18, Close, 3131.95\n",
      "2023-08-21, Close, 3092.98\n",
      "2023-08-22, Close, 3120.33\n",
      "2023-08-23, Close, 3078.40\n",
      "2023-08-24, Close, 3082.24\n",
      "2023-08-25, Close, 3064.07\n",
      "2023-08-28, Close, 3098.64\n",
      "2023-08-29, Close, 3135.89\n",
      "2023-08-30, Close, 3137.14\n",
      "2023-08-30, BUY CREATE, 3137.14\n",
      "2023-08-31, BUY EXECUTED, Price: 3134.55, Cost: 31345.50, Comm 0.00\n",
      "2023-08-31, Close, 3119.88\n",
      "2023-08-31, SELL CREATE, 3119.88\n",
      "2023-09-01, SELL EXECUTED, Price: 3126.02, Cost: 31345.50, Comm 0.00\n",
      "2023-09-01, OPERATION PROFIT, GROSS -85.30, NET -85.30\n",
      "2023-09-01, Close, 3133.25\n",
      "2023-09-01, BUY CREATE, 3133.25\n",
      "2023-09-04, BUY EXECUTED, Price: 3145.94, Cost: 31459.40, Comm 0.00\n",
      "2023-09-04, Close, 3177.06\n",
      "2023-09-05, Close, 3154.37\n",
      "2023-09-06, Close, 3158.08\n",
      "2023-09-07, Close, 3122.35\n",
      "2023-09-08, Close, 3116.72\n",
      "2023-09-08, SELL CREATE, 3116.72\n",
      "2023-09-11, SELL EXECUTED, Price: 3119.61, Cost: 31459.40, Comm 0.00\n",
      "2023-09-11, OPERATION PROFIT, GROSS -263.30, NET -263.30\n",
      "2023-09-11, Close, 3142.78\n",
      "2023-09-11, BUY CREATE, 3142.78\n",
      "2023-09-12, BUY EXECUTED, Price: 3140.34, Cost: 31403.40, Comm 0.00\n",
      "2023-09-12, Close, 3137.06\n",
      "2023-09-13, Close, 3123.07\n",
      "2023-09-13, SELL CREATE, 3123.07\n",
      "2023-09-14, SELL EXECUTED, Price: 3124.85, Cost: 31403.40, Comm 0.00\n",
      "2023-09-14, OPERATION PROFIT, GROSS -154.90, NET -154.90\n",
      "2023-09-14, Close, 3126.55\n",
      "2023-09-15, Close, 3117.74\n",
      "2023-09-18, Close, 3125.93\n",
      "2023-09-19, Close, 3124.96\n",
      "2023-09-20, Close, 3108.57\n",
      "2023-09-21, Close, 3084.70\n",
      "2023-09-22, Close, 3132.43\n",
      "2023-09-22, BUY CREATE, 3132.43\n",
      "2023-09-25, BUY EXECUTED, Price: 3131.21, Cost: 31312.10, Comm 0.00\n",
      "2023-09-25, Close, 3115.61\n",
      "2023-09-25, SELL CREATE, 3115.61\n",
      "2023-09-26, SELL EXECUTED, Price: 3114.02, Cost: 31312.10, Comm 0.00\n",
      "2023-09-26, OPERATION PROFIT, GROSS -171.90, NET -171.90\n",
      "2023-09-26, Close, 3102.27\n",
      "2023-09-27, Close, 3107.32\n",
      "2023-09-28, Close, 3110.48\n",
      "2023-10-09, Close, 3096.92\n",
      "2023-10-10, Close, 3075.24\n",
      "2023-10-11, Close, 3078.96\n",
      "2023-10-12, Close, 3107.90\n",
      "2023-10-12, BUY CREATE, 3107.90\n",
      "2023-10-13, BUY EXECUTED, Price: 3092.00, Cost: 30920.00, Comm 0.00\n",
      "2023-10-13, Close, 3088.10\n",
      "2023-10-13, SELL CREATE, 3088.10\n",
      "2023-10-16, SELL EXECUTED, Price: 3091.06, Cost: 30920.00, Comm 0.00\n",
      "2023-10-16, OPERATION PROFIT, GROSS -9.40, NET -9.40\n",
      "2023-10-16, Close, 3073.81\n",
      "2023-10-17, Close, 3083.50\n",
      "2023-10-18, Close, 3058.71\n",
      "2023-10-19, Close, 3005.39\n",
      "2023-10-20, Close, 2983.06\n",
      "2023-10-23, Close, 2939.29\n",
      "2023-10-24, Close, 2962.24\n",
      "2023-10-25, Close, 2974.11\n",
      "2023-10-26, Close, 2988.30\n",
      "2023-10-27, Close, 3017.78\n",
      "2023-10-30, Close, 3021.55\n",
      "2023-10-31, Close, 3018.77\n",
      "2023-11-01, Close, 3023.08\n",
      "2023-11-01, BUY CREATE, 3023.08\n",
      "2023-11-02, BUY EXECUTED, Price: 3028.67, Cost: 30286.70, Comm 0.00\n",
      "2023-11-02, Close, 3009.41\n",
      "2023-11-02, SELL CREATE, 3009.41\n",
      "2023-11-03, SELL EXECUTED, Price: 3012.47, Cost: 30286.70, Comm 0.00\n",
      "2023-11-03, OPERATION PROFIT, GROSS -162.00, NET -162.00\n",
      "2023-11-03, Close, 3030.80\n",
      "2023-11-03, BUY CREATE, 3030.80\n",
      "2023-11-06, BUY EXECUTED, Price: 3047.13, Cost: 30471.30, Comm 0.00\n",
      "2023-11-06, Close, 3058.41\n",
      "2023-11-07, Close, 3057.27\n",
      "2023-11-08, Close, 3052.37\n",
      "2023-11-09, Close, 3053.28\n",
      "2023-11-10, Close, 3038.97\n",
      "2023-11-13, Close, 3046.53\n",
      "2023-11-14, Close, 3056.07\n",
      "2023-11-15, Close, 3072.83\n",
      "2023-11-16, Close, 3050.93\n",
      "2023-11-17, Close, 3054.37\n",
      "2023-11-20, Close, 3068.32\n",
      "2023-11-21, Close, 3067.93\n",
      "2023-11-22, Close, 3043.61\n",
      "2023-11-22, SELL CREATE, 3043.61\n",
      "2023-11-23, SELL EXECUTED, Price: 3041.68, Cost: 30471.30, Comm 0.00\n",
      "2023-11-23, OPERATION PROFIT, GROSS -54.50, NET -54.50\n",
      "2023-11-23, Close, 3061.86\n",
      "2023-11-23, BUY CREATE, 3061.86\n",
      "2023-11-24, BUY EXECUTED, Price: 3060.33, Cost: 30603.30, Comm 0.00\n",
      "2023-11-24, Close, 3040.97\n",
      "2023-11-24, SELL CREATE, 3040.97\n",
      "2023-11-27, SELL EXECUTED, Price: 3038.19, Cost: 30603.30, Comm 0.00\n",
      "2023-11-27, OPERATION PROFIT, GROSS -221.40, NET -221.40\n",
      "2023-11-27, Close, 3031.70\n",
      "2023-11-28, Close, 3038.55\n",
      "2023-11-29, Close, 3021.69\n",
      "2023-11-30, Close, 3029.67\n",
      "2023-12-01, Close, 3031.64\n",
      "2023-12-04, Close, 3022.91\n",
      "2023-12-05, Close, 2972.30\n",
      "2023-12-06, Close, 2968.93\n",
      "2023-12-07, Close, 2966.21\n",
      "2023-12-08, Close, 2969.56\n",
      "2023-12-11, Close, 2991.44\n",
      "2023-12-12, Close, 3003.44\n",
      "2023-12-13, Close, 2968.76\n",
      "2023-12-14, Close, 2958.99\n",
      "2023-12-15, Close, 2942.56\n",
      "2023-12-18, Close, 2930.80\n",
      "2023-12-19, Close, 2932.39\n",
      "2023-12-20, Close, 2902.11\n",
      "2023-12-21, Close, 2918.72\n",
      "2023-12-22, Close, 2914.77\n",
      "2023-12-25, Close, 2918.81\n",
      "2023-12-26, Close, 2898.88\n",
      "2023-12-27, Close, 2914.61\n",
      "2023-12-28, Close, 2954.70\n",
      "2023-12-28, BUY CREATE, 2954.70\n",
      "2023-12-29, BUY EXECUTED, Price: 2950.84, Cost: 29508.40, Comm 0.00\n",
      "2023-12-29, Close, 2974.94\n",
      "2024-01-02, Close, 2962.28\n",
      "2024-01-03, Close, 2967.25\n",
      "2024-01-04, Close, 2954.35\n",
      "2024-01-05, Close, 2929.18\n",
      "2024-01-05, SELL CREATE, 2929.18\n",
      "2024-01-08, SELL EXECUTED, Price: 2923.46, Cost: 29508.40, Comm 0.00\n",
      "2024-01-08, OPERATION PROFIT, GROSS -273.80, NET -273.80\n",
      "2024-01-08, Close, 2887.54\n",
      "2024-01-09, Close, 2893.25\n",
      "2024-01-10, Close, 2877.70\n",
      "2024-01-11, Close, 2886.65\n",
      "2024-01-12, Close, 2881.98\n",
      "2024-01-15, Close, 2886.29\n",
      "2024-01-16, Close, 2893.99\n",
      "2024-01-17, Close, 2833.61\n",
      "2024-01-18, Close, 2845.78\n",
      "2024-01-19, Close, 2832.28\n",
      "2024-01-22, Close, 2756.34\n",
      "2024-01-23, Close, 2770.98\n",
      "2024-01-24, Close, 2820.77\n",
      "2024-01-25, Close, 2906.11\n",
      "2024-01-25, BUY CREATE, 2906.11\n",
      "2024-01-26, BUY EXECUTED, Price: 2897.92, Cost: 28979.20, Comm 0.00\n",
      "2024-01-26, Close, 2910.22\n",
      "2024-01-29, Close, 2883.36\n",
      "2024-01-30, Close, 2830.53\n",
      "2024-01-30, SELL CREATE, 2830.53\n",
      "2024-01-31, SELL EXECUTED, Price: 2815.50, Cost: 28979.20, Comm 0.00\n",
      "2024-01-31, OPERATION PROFIT, GROSS -824.20, NET -824.20\n",
      "2024-01-31, Close, 2788.55\n",
      "2024-02-01, Close, 2770.74\n",
      "2024-02-02, Close, 2730.15\n",
      "2024-02-05, Close, 2702.19\n",
      "2024-02-06, Close, 2789.49\n",
      "2024-02-07, Close, 2829.70\n",
      "2024-02-07, BUY CREATE, 2829.70\n",
      "2024-02-08, BUY EXECUTED, Price: 2832.49, Cost: 28324.90, Comm 0.00\n",
      "2024-02-08, Close, 2865.90\n",
      "2024-02-19, Close, 2910.54\n",
      "2024-02-20, Close, 2922.73\n",
      "2024-02-21, Close, 2950.96\n",
      "2024-02-22, Close, 2988.36\n",
      "2024-02-23, Close, 3004.88\n",
      "2024-02-26, Close, 2977.02\n",
      "2024-02-27, Close, 3015.48\n",
      "2024-02-28, Close, 2957.85\n",
      "2024-02-29, Close, 3015.17\n",
      "2024-03-01, Close, 3027.02\n",
      "2024-03-04, Close, 3039.31\n",
      "2024-03-05, Close, 3047.79\n",
      "2024-03-06, Close, 3039.93\n",
      "2024-03-07, Close, 3027.40\n",
      "2024-03-08, Close, 3046.02\n",
      "2024-03-11, Close, 3068.46\n",
      "2024-03-12, Close, 3055.94\n",
      "2024-03-13, Close, 3043.83\n",
      "2024-03-14, Close, 3038.23\n",
      "2024-03-15, Close, 3054.64\n",
      "2024-03-18, Close, 3084.93\n",
      "2024-03-19, Close, 3062.76\n",
      "2024-03-20, Close, 3079.69\n",
      "2024-03-21, Close, 3077.11\n",
      "2024-03-22, Close, 3048.03\n",
      "2024-03-22, SELL CREATE, 3048.03\n",
      "2024-03-25, SELL EXECUTED, Price: 3040.50, Cost: 28324.90, Comm 0.00\n",
      "2024-03-25, OPERATION PROFIT, GROSS 2080.10, NET 2080.10\n",
      "2024-03-25, Close, 3026.31\n",
      "2024-03-26, Close, 3031.48\n",
      "2024-03-27, Close, 2993.14\n",
      "2024-03-28, Close, 3010.66\n",
      "2024-03-29, Close, 3041.17\n",
      "2024-04-01, Close, 3077.38\n",
      "2024-04-01, BUY CREATE, 3077.38\n",
      "2024-04-02, BUY EXECUTED, Price: 3076.88, Cost: 30768.80, Comm 0.00\n",
      "2024-04-02, Close, 3074.96\n",
      "2024-04-03, Close, 3069.30\n",
      "2024-04-08, Close, 3047.05\n",
      "2024-04-08, SELL CREATE, 3047.05\n",
      "2024-04-09, SELL EXECUTED, Price: 3044.61, Cost: 30768.80, Comm 0.00\n",
      "2024-04-09, OPERATION PROFIT, GROSS -322.70, NET -322.70\n",
      "2024-04-09, Close, 3048.54\n",
      "2024-04-10, Close, 3027.33\n",
      "2024-04-11, Close, 3034.25\n",
      "2024-04-12, Close, 3019.47\n",
      "2024-04-15, Close, 3057.38\n",
      "2024-04-15, BUY CREATE, 3057.38\n",
      "2024-04-16, BUY EXECUTED, Price: 3043.55, Cost: 30435.50, Comm 0.00\n",
      "2024-04-16, Close, 3007.07\n",
      "2024-04-16, SELL CREATE, 3007.07\n",
      "2024-04-17, SELL EXECUTED, Price: 3008.06, Cost: 30435.50, Comm 0.00\n",
      "2024-04-17, OPERATION PROFIT, GROSS -354.90, NET -354.90\n",
      "2024-04-17, Close, 3071.38\n",
      "2024-04-17, BUY CREATE, 3071.38\n",
      "2024-04-18, BUY EXECUTED, Price: 3060.03, Cost: 30600.30, Comm 0.00\n",
      "2024-04-18, Close, 3074.23\n",
      "2024-04-19, Close, 3065.26\n",
      "2024-04-22, Close, 3044.59\n",
      "2024-04-22, SELL CREATE, 3044.59\n",
      "2024-04-23, SELL EXECUTED, Price: 3038.61, Cost: 30600.30, Comm 0.00\n",
      "2024-04-23, OPERATION PROFIT, GROSS -214.20, NET -214.20\n",
      "2024-04-23, Close, 3021.98\n",
      "2024-04-24, Close, 3044.82\n",
      "2024-04-25, Close, 3052.90\n",
      "2024-04-25, BUY CREATE, 3052.90\n",
      "2024-04-26, BUY EXECUTED, Price: 3054.98, Cost: 30549.80, Comm 0.00\n",
      "2024-04-26, Close, 3088.64\n",
      "2024-04-29, Close, 3113.04\n",
      "2024-04-30, Close, 3104.82\n",
      "2024-05-06, Close, 3140.72\n",
      "2024-05-07, Close, 3147.74\n",
      "2024-05-08, Close, 3128.48\n",
      "2024-05-09, Close, 3154.32\n",
      "2024-05-10, Close, 3154.55\n",
      "2024-05-13, Close, 3148.02\n",
      "2024-05-14, Close, 3145.77\n",
      "2024-05-15, Close, 3119.90\n",
      "2024-05-16, Close, 3122.40\n",
      "2024-05-17, Close, 3154.03\n",
      "2024-05-20, Close, 3171.15\n",
      "2024-05-21, Close, 3157.97\n",
      "2024-05-22, Close, 3158.54\n",
      "2024-05-23, Close, 3116.39\n",
      "2024-05-23, SELL CREATE, 3116.39\n",
      "2024-05-24, SELL EXECUTED, Price: 3110.75, Cost: 30549.80, Comm 0.00\n",
      "2024-05-24, OPERATION PROFIT, GROSS 557.70, NET 557.70\n",
      "2024-05-24, Close, 3088.87\n",
      "2024-05-27, Close, 3124.04\n",
      "2024-05-28, Close, 3109.57\n",
      "2024-05-29, Close, 3111.02\n",
      "2024-05-30, Close, 3091.68\n",
      "2024-05-31, Close, 3086.81\n",
      "2024-06-03, Close, 3078.49\n",
      "2024-06-04, Close, 3091.20\n",
      "2024-06-05, Close, 3065.40\n",
      "2024-06-06, Close, 3048.79\n",
      "2024-06-07, Close, 3051.28\n",
      "2024-06-11, Close, 3028.04\n",
      "2024-06-12, Close, 3037.47\n",
      "2024-06-13, Close, 3028.92\n",
      "2024-06-14, Close, 3032.63\n",
      "2024-06-17, Close, 3015.89\n",
      "Final Portfolio Value: 98322.90\n"
     ]
    }
   ],
   "source": [
    "from __future__ import (absolute_import, division, print_function,\n",
    "                        unicode_literals)\n",
    "\n",
    "import datetime  # For datetime objects\n",
    "import os.path  # To manage paths\n",
    "import sys  # To find out the script name (in argv[0])\n",
    "\n",
    "# Import the backtrader platform\n",
    "import backtrader as bt\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "\n",
    "# Create a Stratey\n",
    "class TestStrategy(bt.Strategy):\n",
    "    params = (\n",
    "        ('maperiod', 15),\n",
    "    )\n",
    "\n",
    "    def log(self, txt, dt=None):\n",
    "        ''' Logging function fot this strategy'''\n",
    "        dt = dt or self.datas[0].datetime.date(0)\n",
    "        print('%s, %s' % (dt.isoformat(), txt))\n",
    "\n",
    "    def __init__(self):\n",
    "        # Keep a reference to the \"close\" line in the data[0] dataseries\n",
    "        self.dataclose = self.datas[0].close\n",
    "\n",
    "        # To keep track of pending orders and buy price/commission\n",
    "        self.order = None\n",
    "        self.buyprice = None\n",
    "        self.buycomm = None\n",
    "\n",
    "        # Add a MovingAverageSimple indicator\n",
    "        self.sma = bt.indicators.SimpleMovingAverage(\n",
    "            self.datas[0], period=self.params.maperiod)\n",
    "\n",
    "    def notify_order(self, order):\n",
    "        if order.status in [order.Submitted, order.Accepted]:\n",
    "            # Buy/Sell order submitted/accepted to/by broker - Nothing to do\n",
    "            return\n",
    "\n",
    "        # Check if an order has been completed\n",
    "        # Attention: broker could reject order if not enough cash\n",
    "        if order.status in [order.Completed]:\n",
    "            if order.isbuy():\n",
    "                self.log(\n",
    "                    'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %\n",
    "                    (order.executed.price,\n",
    "                     order.executed.value,\n",
    "                     order.executed.comm))\n",
    "\n",
    "                self.buyprice = order.executed.price\n",
    "                self.buycomm = order.executed.comm\n",
    "            else:  # Sell\n",
    "                self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %\n",
    "                         (order.executed.price,\n",
    "                          order.executed.value,\n",
    "                          order.executed.comm))\n",
    "\n",
    "            self.bar_executed = len(self)\n",
    "\n",
    "        elif order.status in [order.Canceled, order.Margin, order.Rejected]:\n",
    "            self.log('Order Canceled/Margin/Rejected')\n",
    "\n",
    "        self.order = None\n",
    "\n",
    "    def notify_trade(self, trade):\n",
    "        if not trade.isclosed:\n",
    "            return\n",
    "\n",
    "        self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %\n",
    "                 (trade.pnl, trade.pnlcomm))\n",
    "\n",
    "    def next(self):\n",
    "        # Simply log the closing price of the series from the reference\n",
    "        self.log('Close, %.2f' % self.dataclose[0])\n",
    "\n",
    "        # Check if an order is pending ... if yes, we cannot send a 2nd one\n",
    "        if self.order:\n",
    "            return\n",
    "\n",
    "        # Check if we are in the market\n",
    "        if not self.position:\n",
    "\n",
    "            # Not yet ... we MIGHT BUY if ...\n",
    "            if self.dataclose[0] > self.sma[0]:\n",
    "\n",
    "                # BUY, BUY, BUY!!! (with all possible default parameters)\n",
    "                self.log('BUY CREATE, %.2f' % self.dataclose[0])\n",
    "\n",
    "                # Keep track of the created order to avoid a 2nd order\n",
    "                self.order = self.buy()\n",
    "\n",
    "        else:\n",
    "\n",
    "            if self.dataclose[0] < self.sma[0]:\n",
    "                # SELL, SELL, SELL!!! (with all possible default parameters)\n",
    "                self.log('SELL CREATE, %.2f' % self.dataclose[0])\n",
    "\n",
    "                # Keep track of the created order to avoid a 2nd order\n",
    "                self.order = self.sell()\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    # Create a cerebro entity\n",
    "    cerebro = bt.Cerebro()\n",
    "\n",
    "    # Add a strategy\n",
    "    cerebro.addstrategy(TestStrategy)\n",
    "\n",
    "    # Datas are in a subfolder of the samples. Need to find where the script is\n",
    "    # because it could have been called from anywhere\n",
    "    # modpath = os.path.dirname(os.path.abspath(sys.argv[0]))\n",
    "    # modpath = 'C:\\\\Users\\\\bww\\gitee\\\\stock\\\\backtrader'\n",
    "    # datapath = os.path.join(modpath, '000001.SS.csv')\n",
    "    datapath='E:\\\\stock\\\\stock\\\\backtrader\\\\000001.SS.csv'\n",
    "\n",
    "    # Create a Data Feed\n",
    "    data = bt.feeds.YahooFinanceCSVData(\n",
    "        dataname=datapath,\n",
    "        # Do not pass values before this date\n",
    "        fromdate=datetime.datetime(2000, 1, 1),\n",
    "        # Do not pass values before this date\n",
    "        todate=datetime.datetime(2024, 12, 31),\n",
    "        # Do not pass values after this date\n",
    "        reverse=False)\n",
    "\n",
    "    # Add the Data Feed to Cerebro\n",
    "    cerebro.adddata(data)\n",
    "\n",
    "    # Set our desired cash start\n",
    "    cerebro.broker.setcash(100000.0)\n",
    "\n",
    "    # Add a FixedSize sizer according to the stake\n",
    "    cerebro.addsizer(bt.sizers.FixedSize, stake=10)\n",
    "\n",
    "    # Set the commission\n",
    "    cerebro.broker.setcommission(commission=0.0)\n",
    "\n",
    "    # Print out the starting conditions\n",
    "    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())\n",
    "\n",
    "    # Run over everything\n",
    "    cerebro.run()\n",
    "\n",
    "  \n",
    "    # Print out the final result\n",
    "    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": "/* Put everything inside the global mpl namespace */\n/* global mpl */\nwindow.mpl = {};\n\nmpl.get_websocket_type = function () {\n    if (typeof WebSocket !== 'undefined') {\n        return WebSocket;\n    } else if (typeof MozWebSocket !== 'undefined') {\n        return MozWebSocket;\n    } else {\n        alert(\n            'Your browser does not have WebSocket support. ' +\n                'Please try Chrome, Safari or Firefox ≥ 6. ' +\n                'Firefox 4 and 5 are also supported but you ' +\n                'have to enable WebSockets in about:config.'\n        );\n    }\n};\n\nmpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n    this.id = figure_id;\n\n    this.ws = websocket;\n\n    this.supports_binary = this.ws.binaryType !== undefined;\n\n    if (!this.supports_binary) {\n        var warnings = document.getElementById('mpl-warnings');\n        if (warnings) {\n            warnings.style.display = 'block';\n            warnings.textContent =\n                'This browser does not support binary websocket messages. ' +\n                'Performance may be slow.';\n        }\n    }\n\n    this.imageObj = new Image();\n\n    this.context = undefined;\n    this.message = undefined;\n    this.canvas = undefined;\n    this.rubberband_canvas = undefined;\n    this.rubberband_context = undefined;\n    this.format_dropdown = undefined;\n\n    this.image_mode = 'full';\n\n    this.root = document.createElement('div');\n    this.root.setAttribute('style', 'display: inline-block');\n    this._root_extra_style(this.root);\n\n    parent_element.appendChild(this.root);\n\n    this._init_header(this);\n    this._init_canvas(this);\n    this._init_toolbar(this);\n\n    var fig = this;\n\n    this.waiting = false;\n\n    this.ws.onopen = function () {\n        fig.send_message('supports_binary', { value: fig.supports_binary });\n        fig.send_message('send_image_mode', {});\n        if (fig.ratio !== 1) {\n            fig.send_message('set_device_pixel_ratio', {\n                device_pixel_ratio: fig.ratio,\n            });\n        }\n        fig.send_message('refresh', {});\n    };\n\n    this.imageObj.onload = function () {\n        if (fig.image_mode === 'full') {\n            // Full images could contain transparency (where diff images\n            // almost always do), so we need to clear the canvas so that\n            // there is no ghosting.\n            fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n        }\n        fig.context.drawImage(fig.imageObj, 0, 0);\n    };\n\n    this.imageObj.onunload = function () {\n        fig.ws.close();\n    };\n\n    this.ws.onmessage = this._make_on_message_function(this);\n\n    this.ondownload = ondownload;\n};\n\nmpl.figure.prototype._init_header = function () {\n    var titlebar = document.createElement('div');\n    titlebar.classList =\n        'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n    var titletext = document.createElement('div');\n    titletext.classList = 'ui-dialog-title';\n    titletext.setAttribute(\n        'style',\n        'width: 100%; text-align: center; padding: 3px;'\n    );\n    titlebar.appendChild(titletext);\n    this.root.appendChild(titlebar);\n    this.header = titletext;\n};\n\nmpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._init_canvas = function () {\n    var fig = this;\n\n    var canvas_div = (this.canvas_div = document.createElement('div'));\n    canvas_div.setAttribute('tabindex', '0');\n    canvas_div.setAttribute(\n        'style',\n        'border: 1px solid #ddd;' +\n            'box-sizing: content-box;' +\n            'clear: both;' +\n            'min-height: 1px;' +\n            'min-width: 1px;' +\n            'outline: 0;' +\n            'overflow: hidden;' +\n            'position: relative;' +\n            'resize: both;' +\n            'z-index: 2;'\n    );\n\n    function on_keyboard_event_closure(name) {\n        return function (event) {\n            return fig.key_event(event, name);\n        };\n    }\n\n    canvas_div.addEventListener(\n        'keydown',\n        on_keyboard_event_closure('key_press')\n    );\n    canvas_div.addEventListener(\n        'keyup',\n        on_keyboard_event_closure('key_release')\n    );\n\n    this._canvas_extra_style(canvas_div);\n    this.root.appendChild(canvas_div);\n\n    var canvas = (this.canvas = document.createElement('canvas'));\n    canvas.classList.add('mpl-canvas');\n    canvas.setAttribute(\n        'style',\n        'box-sizing: content-box;' +\n            'pointer-events: none;' +\n            'position: relative;' +\n            'z-index: 0;'\n    );\n\n    this.context = canvas.getContext('2d');\n\n    var backingStore =\n        this.context.backingStorePixelRatio ||\n        this.context.webkitBackingStorePixelRatio ||\n        this.context.mozBackingStorePixelRatio ||\n        this.context.msBackingStorePixelRatio ||\n        this.context.oBackingStorePixelRatio ||\n        this.context.backingStorePixelRatio ||\n        1;\n\n    this.ratio = (window.devicePixelRatio || 1) / backingStore;\n\n    var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n        'canvas'\n    ));\n    rubberband_canvas.setAttribute(\n        'style',\n        'box-sizing: content-box;' +\n            'left: 0;' +\n            'pointer-events: none;' +\n            'position: absolute;' +\n            'top: 0;' +\n            'z-index: 1;'\n    );\n\n    // Apply a ponyfill if ResizeObserver is not implemented by browser.\n    if (this.ResizeObserver === undefined) {\n        if (window.ResizeObserver !== undefined) {\n            this.ResizeObserver = window.ResizeObserver;\n        } else {\n            var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n            this.ResizeObserver = obs.ResizeObserver;\n        }\n    }\n\n    this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n        // There's no need to resize if the WebSocket is not connected:\n        // - If it is still connecting, then we will get an initial resize from\n        //   Python once it connects.\n        // - If it has disconnected, then resizing will clear the canvas and\n        //   never get anything back to refill it, so better to not resize and\n        //   keep something visible.\n        if (fig.ws.readyState != 1) {\n            return;\n        }\n        var nentries = entries.length;\n        for (var i = 0; i < nentries; i++) {\n            var entry = entries[i];\n            var width, height;\n            if (entry.contentBoxSize) {\n                if (entry.contentBoxSize instanceof Array) {\n                    // Chrome 84 implements new version of spec.\n                    width = entry.contentBoxSize[0].inlineSize;\n                    height = entry.contentBoxSize[0].blockSize;\n                } else {\n                    // Firefox implements old version of spec.\n                    width = entry.contentBoxSize.inlineSize;\n                    height = entry.contentBoxSize.blockSize;\n                }\n            } else {\n                // Chrome <84 implements even older version of spec.\n                width = entry.contentRect.width;\n                height = entry.contentRect.height;\n            }\n\n            // Keep the size of the canvas and rubber band canvas in sync with\n            // the canvas container.\n            if (entry.devicePixelContentBoxSize) {\n                // Chrome 84 implements new version of spec.\n                canvas.setAttribute(\n                    'width',\n                    entry.devicePixelContentBoxSize[0].inlineSize\n                );\n                canvas.setAttribute(\n                    'height',\n                    entry.devicePixelContentBoxSize[0].blockSize\n                );\n            } else {\n                canvas.setAttribute('width', width * fig.ratio);\n                canvas.setAttribute('height', height * fig.ratio);\n            }\n            /* This rescales the canvas back to display pixels, so that it\n             * appears correct on HiDPI screens. */\n            canvas.style.width = width + 'px';\n            canvas.style.height = height + 'px';\n\n            rubberband_canvas.setAttribute('width', width);\n            rubberband_canvas.setAttribute('height', height);\n\n            // And update the size in Python. We ignore the initial 0/0 size\n            // that occurs as the element is placed into the DOM, which should\n            // otherwise not happen due to the minimum size styling.\n            if (width != 0 && height != 0) {\n                fig.request_resize(width, height);\n            }\n        }\n    });\n    this.resizeObserverInstance.observe(canvas_div);\n\n    function on_mouse_event_closure(name) {\n        /* User Agent sniffing is bad, but WebKit is busted:\n         * https://bugs.webkit.org/show_bug.cgi?id=144526\n         * https://bugs.webkit.org/show_bug.cgi?id=181818\n         * The worst that happens here is that they get an extra browser\n         * selection when dragging, if this check fails to catch them.\n         */\n        var UA = navigator.userAgent;\n        var isWebKit = /AppleWebKit/.test(UA) && !/Chrome/.test(UA);\n        if(isWebKit) {\n            return function (event) {\n                /* This prevents the web browser from automatically changing to\n                 * the text insertion cursor when the button is pressed. We\n                 * want to control all of the cursor setting manually through\n                 * the 'cursor' event from matplotlib */\n                event.preventDefault()\n                return fig.mouse_event(event, name);\n            };\n        } else {\n            return function (event) {\n                return fig.mouse_event(event, name);\n            };\n        }\n    }\n\n    canvas_div.addEventListener(\n        'mousedown',\n        on_mouse_event_closure('button_press')\n    );\n    canvas_div.addEventListener(\n        'mouseup',\n        on_mouse_event_closure('button_release')\n    );\n    canvas_div.addEventListener(\n        'dblclick',\n        on_mouse_event_closure('dblclick')\n    );\n    // Throttle sequential mouse events to 1 every 20ms.\n    canvas_div.addEventListener(\n        'mousemove',\n        on_mouse_event_closure('motion_notify')\n    );\n\n    canvas_div.addEventListener(\n        'mouseenter',\n        on_mouse_event_closure('figure_enter')\n    );\n    canvas_div.addEventListener(\n        'mouseleave',\n        on_mouse_event_closure('figure_leave')\n    );\n\n    canvas_div.addEventListener('wheel', function (event) {\n        if (event.deltaY < 0) {\n            event.step = 1;\n        } else {\n            event.step = -1;\n        }\n        on_mouse_event_closure('scroll')(event);\n    });\n\n    canvas_div.appendChild(canvas);\n    canvas_div.appendChild(rubberband_canvas);\n\n    this.rubberband_context = rubberband_canvas.getContext('2d');\n    this.rubberband_context.strokeStyle = '#000000';\n\n    this._resize_canvas = function (width, height, forward) {\n        if (forward) {\n            canvas_div.style.width = width + 'px';\n            canvas_div.style.height = height + 'px';\n        }\n    };\n\n    // Disable right mouse context menu.\n    canvas_div.addEventListener('contextmenu', function (_e) {\n        event.preventDefault();\n        return false;\n    });\n\n    function set_focus() {\n        canvas.focus();\n        canvas_div.focus();\n    }\n\n    window.setTimeout(set_focus, 100);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n    var fig = this;\n\n    var toolbar = document.createElement('div');\n    toolbar.classList = 'mpl-toolbar';\n    this.root.appendChild(toolbar);\n\n    function on_click_closure(name) {\n        return function (_event) {\n            return fig.toolbar_button_onclick(name);\n        };\n    }\n\n    function on_mouseover_closure(tooltip) {\n        return function (event) {\n            if (!event.currentTarget.disabled) {\n                return fig.toolbar_button_onmouseover(tooltip);\n            }\n        };\n    }\n\n    fig.buttons = {};\n    var buttonGroup = document.createElement('div');\n    buttonGroup.classList = 'mpl-button-group';\n    for (var toolbar_ind in mpl.toolbar_items) {\n        var name = mpl.toolbar_items[toolbar_ind][0];\n        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n        var image = mpl.toolbar_items[toolbar_ind][2];\n        var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n        if (!name) {\n            /* Instead of a spacer, we start a new button group. */\n            if (buttonGroup.hasChildNodes()) {\n                toolbar.appendChild(buttonGroup);\n            }\n            buttonGroup = document.createElement('div');\n            buttonGroup.classList = 'mpl-button-group';\n            continue;\n        }\n\n        var button = (fig.buttons[name] = document.createElement('button'));\n        button.classList = 'mpl-widget';\n        button.setAttribute('role', 'button');\n        button.setAttribute('aria-disabled', 'false');\n        button.addEventListener('click', on_click_closure(method_name));\n        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n\n        var icon_img = document.createElement('img');\n        icon_img.src = '_images/' + image + '.png';\n        icon_img.srcset = '_images/' + image + '_large.png 2x';\n        icon_img.alt = tooltip;\n        button.appendChild(icon_img);\n\n        buttonGroup.appendChild(button);\n    }\n\n    if (buttonGroup.hasChildNodes()) {\n        toolbar.appendChild(buttonGroup);\n    }\n\n    var fmt_picker = document.createElement('select');\n    fmt_picker.classList = 'mpl-widget';\n    toolbar.appendChild(fmt_picker);\n    this.format_dropdown = fmt_picker;\n\n    for (var ind in mpl.extensions) {\n        var fmt = mpl.extensions[ind];\n        var option = document.createElement('option');\n        option.selected = fmt === mpl.default_extension;\n        option.innerHTML = fmt;\n        fmt_picker.appendChild(option);\n    }\n\n    var status_bar = document.createElement('span');\n    status_bar.classList = 'mpl-message';\n    toolbar.appendChild(status_bar);\n    this.message = status_bar;\n};\n\nmpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n    // which will in turn request a refresh of the image.\n    this.send_message('resize', { width: x_pixels, height: y_pixels });\n};\n\nmpl.figure.prototype.send_message = function (type, properties) {\n    properties['type'] = type;\n    properties['figure_id'] = this.id;\n    this.ws.send(JSON.stringify(properties));\n};\n\nmpl.figure.prototype.send_draw_message = function () {\n    if (!this.waiting) {\n        this.waiting = true;\n        this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n    }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n    var format_dropdown = fig.format_dropdown;\n    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n    fig.ondownload(fig, format);\n};\n\nmpl.figure.prototype.handle_resize = function (fig, msg) {\n    var size = msg['size'];\n    if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n        fig._resize_canvas(size[0], size[1], msg['forward']);\n        fig.send_message('refresh', {});\n    }\n};\n\nmpl.figure.prototype.handle_rubberband = function (fig, msg) {\n    var x0 = msg['x0'] / fig.ratio;\n    var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n    var x1 = msg['x1'] / fig.ratio;\n    var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n    x0 = Math.floor(x0) + 0.5;\n    y0 = Math.floor(y0) + 0.5;\n    x1 = Math.floor(x1) + 0.5;\n    y1 = Math.floor(y1) + 0.5;\n    var min_x = Math.min(x0, x1);\n    var min_y = Math.min(y0, y1);\n    var width = Math.abs(x1 - x0);\n    var height = Math.abs(y1 - y0);\n\n    fig.rubberband_context.clearRect(\n        0,\n        0,\n        fig.canvas.width / fig.ratio,\n        fig.canvas.height / fig.ratio\n    );\n\n    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n};\n\nmpl.figure.prototype.handle_figure_label = function (fig, msg) {\n    // Updates the figure title.\n    fig.header.textContent = msg['label'];\n};\n\nmpl.figure.prototype.handle_cursor = function (fig, msg) {\n    fig.canvas_div.style.cursor = msg['cursor'];\n};\n\nmpl.figure.prototype.handle_message = function (fig, msg) {\n    fig.message.textContent = msg['message'];\n};\n\nmpl.figure.prototype.handle_draw = function (fig, _msg) {\n    // Request the server to send over a new figure.\n    fig.send_draw_message();\n};\n\nmpl.figure.prototype.handle_image_mode = function (fig, msg) {\n    fig.image_mode = msg['mode'];\n};\n\nmpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n    for (var key in msg) {\n        if (!(key in fig.buttons)) {\n            continue;\n        }\n        fig.buttons[key].disabled = !msg[key];\n        fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n    }\n};\n\nmpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n    if (msg['mode'] === 'PAN') {\n        fig.buttons['Pan'].classList.add('active');\n        fig.buttons['Zoom'].classList.remove('active');\n    } else if (msg['mode'] === 'ZOOM') {\n        fig.buttons['Pan'].classList.remove('active');\n        fig.buttons['Zoom'].classList.add('active');\n    } else {\n        fig.buttons['Pan'].classList.remove('active');\n        fig.buttons['Zoom'].classList.remove('active');\n    }\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n    // Called whenever the canvas gets updated.\n    this.send_message('ack', {});\n};\n\n// A function to construct a web socket function for onmessage handling.\n// Called in the figure constructor.\nmpl.figure.prototype._make_on_message_function = function (fig) {\n    return function socket_on_message(evt) {\n        if (evt.data instanceof Blob) {\n            var img = evt.data;\n            if (img.type !== 'image/png') {\n                /* FIXME: We get \"Resource interpreted as Image but\n                 * transferred with MIME type text/plain:\" errors on\n                 * Chrome.  But how to set the MIME type?  It doesn't seem\n                 * to be part of the websocket stream */\n                img.type = 'image/png';\n            }\n\n            /* Free the memory for the previous frames */\n            if (fig.imageObj.src) {\n                (window.URL || window.webkitURL).revokeObjectURL(\n                    fig.imageObj.src\n                );\n            }\n\n            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n                img\n            );\n            fig.updated_canvas_event();\n            fig.waiting = false;\n            return;\n        } else if (\n            typeof evt.data === 'string' &&\n            evt.data.slice(0, 21) === 'data:image/png;base64'\n        ) {\n            fig.imageObj.src = evt.data;\n            fig.updated_canvas_event();\n            fig.waiting = false;\n            return;\n        }\n\n        var msg = JSON.parse(evt.data);\n        var msg_type = msg['type'];\n\n        // Call the  \"handle_{type}\" callback, which takes\n        // the figure and JSON message as its only arguments.\n        try {\n            var callback = fig['handle_' + msg_type];\n        } catch (e) {\n            console.log(\n                \"No handler for the '\" + msg_type + \"' message type: \",\n                msg\n            );\n            return;\n        }\n\n        if (callback) {\n            try {\n                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n                callback(fig, msg);\n            } catch (e) {\n                console.log(\n                    \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n                    e,\n                    e.stack,\n                    msg\n                );\n            }\n        }\n    };\n};\n\nfunction getModifiers(event) {\n    var mods = [];\n    if (event.ctrlKey) {\n        mods.push('ctrl');\n    }\n    if (event.altKey) {\n        mods.push('alt');\n    }\n    if (event.shiftKey) {\n        mods.push('shift');\n    }\n    if (event.metaKey) {\n        mods.push('meta');\n    }\n    return mods;\n}\n\n/*\n * return a copy of an object with only non-object keys\n * we need this to avoid circular references\n * https://stackoverflow.com/a/24161582/3208463\n */\nfunction simpleKeys(original) {\n    return Object.keys(original).reduce(function (obj, key) {\n        if (typeof original[key] !== 'object') {\n            obj[key] = original[key];\n        }\n        return obj;\n    }, {});\n}\n\nmpl.figure.prototype.mouse_event = function (event, name) {\n    if (name === 'button_press') {\n        this.canvas.focus();\n        this.canvas_div.focus();\n    }\n\n    // from https://stackoverflow.com/q/1114465\n    var boundingRect = this.canvas.getBoundingClientRect();\n    var x = (event.clientX - boundingRect.left) * this.ratio;\n    var y = (event.clientY - boundingRect.top) * this.ratio;\n\n    this.send_message(name, {\n        x: x,\n        y: y,\n        button: event.button,\n        step: event.step,\n        modifiers: getModifiers(event),\n        guiEvent: simpleKeys(event),\n    });\n\n    return false;\n};\n\nmpl.figure.prototype._key_event_extra = function (_event, _name) {\n    // Handle any extra behaviour associated with a key event\n};\n\nmpl.figure.prototype.key_event = function (event, name) {\n    // Prevent repeat events\n    if (name === 'key_press') {\n        if (event.key === this._key) {\n            return;\n        } else {\n            this._key = event.key;\n        }\n    }\n    if (name === 'key_release') {\n        this._key = null;\n    }\n\n    var value = '';\n    if (event.ctrlKey && event.key !== 'Control') {\n        value += 'ctrl+';\n    }\n    else if (event.altKey && event.key !== 'Alt') {\n        value += 'alt+';\n    }\n    else if (event.shiftKey && event.key !== 'Shift') {\n        value += 'shift+';\n    }\n\n    value += 'k' + event.key;\n\n    this._key_event_extra(event, name);\n\n    this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n    return false;\n};\n\nmpl.figure.prototype.toolbar_button_onclick = function (name) {\n    if (name === 'download') {\n        this.handle_save(this, null);\n    } else {\n        this.send_message('toolbar_button', { name: name });\n    }\n};\n\nmpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n    this.message.textContent = tooltip;\n};\n\n///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n// prettier-ignore\nvar _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\nmpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\", \"webp\"];\n\nmpl.default_extension = \"png\";/* global mpl */\n\nvar comm_websocket_adapter = function (comm) {\n    // Create a \"websocket\"-like object which calls the given IPython comm\n    // object with the appropriate methods. Currently this is a non binary\n    // socket, so there is still some room for performance tuning.\n    var ws = {};\n\n    ws.binaryType = comm.kernel.ws.binaryType;\n    ws.readyState = comm.kernel.ws.readyState;\n    function updateReadyState(_event) {\n        if (comm.kernel.ws) {\n            ws.readyState = comm.kernel.ws.readyState;\n        } else {\n            ws.readyState = 3; // Closed state.\n        }\n    }\n    comm.kernel.ws.addEventListener('open', updateReadyState);\n    comm.kernel.ws.addEventListener('close', updateReadyState);\n    comm.kernel.ws.addEventListener('error', updateReadyState);\n\n    ws.close = function () {\n        comm.close();\n    };\n    ws.send = function (m) {\n        //console.log('sending', m);\n        comm.send(m);\n    };\n    // Register the callback with on_msg.\n    comm.on_msg(function (msg) {\n        //console.log('receiving', msg['content']['data'], msg);\n        var data = msg['content']['data'];\n        if (data['blob'] !== undefined) {\n            data = {\n                data: new Blob(msg['buffers'], { type: data['blob'] }),\n            };\n        }\n        // Pass the mpl event to the overridden (by mpl) onmessage function.\n        ws.onmessage(data);\n    });\n    return ws;\n};\n\nmpl.mpl_figure_comm = function (comm, msg) {\n    // This is the function which gets called when the mpl process\n    // starts-up an IPython Comm through the \"matplotlib\" channel.\n\n    var id = msg.content.data.id;\n    // Get hold of the div created by the display call when the Comm\n    // socket was opened in Python.\n    var element = document.getElementById(id);\n    var ws_proxy = comm_websocket_adapter(comm);\n\n    function ondownload(figure, _format) {\n        window.open(figure.canvas.toDataURL());\n    }\n\n    var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n\n    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n    // web socket which is closed, not our websocket->open comm proxy.\n    ws_proxy.onopen();\n\n    fig.parent_element = element;\n    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n    if (!fig.cell_info) {\n        console.error('Failed to find cell for figure', id, fig);\n        return;\n    }\n    fig.cell_info[0].output_area.element.on(\n        'cleared',\n        { fig: fig },\n        fig._remove_fig_handler\n    );\n};\n\nmpl.figure.prototype.handle_close = function (fig, msg) {\n    var width = fig.canvas.width / fig.ratio;\n    fig.cell_info[0].output_area.element.off(\n        'cleared',\n        fig._remove_fig_handler\n    );\n    fig.resizeObserverInstance.unobserve(fig.canvas_div);\n\n    // Update the output cell to use the data from the current canvas.\n    fig.push_to_output();\n    var dataURL = fig.canvas.toDataURL();\n    // Re-enable the keyboard manager in IPython - without this line, in FF,\n    // the notebook keyboard shortcuts fail.\n    IPython.keyboard_manager.enable();\n    fig.parent_element.innerHTML =\n        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n    fig.close_ws(fig, msg);\n};\n\nmpl.figure.prototype.close_ws = function (fig, msg) {\n    fig.send_message('closing', msg);\n    // fig.ws.close()\n};\n\nmpl.figure.prototype.push_to_output = function (_remove_interactive) {\n    // Turn the data on the canvas into data in the output cell.\n    var width = this.canvas.width / this.ratio;\n    var dataURL = this.canvas.toDataURL();\n    this.cell_info[1]['text/html'] =\n        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n    // Tell IPython that the notebook contents must change.\n    IPython.notebook.set_dirty(true);\n    this.send_message('ack', {});\n    var fig = this;\n    // Wait a second, then push the new image to the DOM so\n    // that it is saved nicely (might be nice to debounce this).\n    setTimeout(function () {\n        fig.push_to_output();\n    }, 1000);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n    var fig = this;\n\n    var toolbar = document.createElement('div');\n    toolbar.classList = 'btn-toolbar';\n    this.root.appendChild(toolbar);\n\n    function on_click_closure(name) {\n        return function (_event) {\n            return fig.toolbar_button_onclick(name);\n        };\n    }\n\n    function on_mouseover_closure(tooltip) {\n        return function (event) {\n            if (!event.currentTarget.disabled) {\n                return fig.toolbar_button_onmouseover(tooltip);\n            }\n        };\n    }\n\n    fig.buttons = {};\n    var buttonGroup = document.createElement('div');\n    buttonGroup.classList = 'btn-group';\n    var button;\n    for (var toolbar_ind in mpl.toolbar_items) {\n        var name = mpl.toolbar_items[toolbar_ind][0];\n        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n        var image = mpl.toolbar_items[toolbar_ind][2];\n        var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n        if (!name) {\n            /* Instead of a spacer, we start a new button group. */\n            if (buttonGroup.hasChildNodes()) {\n                toolbar.appendChild(buttonGroup);\n            }\n            buttonGroup = document.createElement('div');\n            buttonGroup.classList = 'btn-group';\n            continue;\n        }\n\n        button = fig.buttons[name] = document.createElement('button');\n        button.classList = 'btn btn-default';\n        button.href = '#';\n        button.title = name;\n        button.innerHTML = '<i class=\"fa ' + image + ' fa-lg\"></i>';\n        button.addEventListener('click', on_click_closure(method_name));\n        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n        buttonGroup.appendChild(button);\n    }\n\n    if (buttonGroup.hasChildNodes()) {\n        toolbar.appendChild(buttonGroup);\n    }\n\n    // Add the status bar.\n    var status_bar = document.createElement('span');\n    status_bar.classList = 'mpl-message pull-right';\n    toolbar.appendChild(status_bar);\n    this.message = status_bar;\n\n    // Add the close button to the window.\n    var buttongrp = document.createElement('div');\n    buttongrp.classList = 'btn-group inline pull-right';\n    button = document.createElement('button');\n    button.classList = 'btn btn-mini btn-primary';\n    button.href = '#';\n    button.title = 'Stop Interaction';\n    button.innerHTML = '<i class=\"fa fa-power-off icon-remove icon-large\"></i>';\n    button.addEventListener('click', function (_evt) {\n        fig.handle_close(fig, {});\n    });\n    button.addEventListener(\n        'mouseover',\n        on_mouseover_closure('Stop Interaction')\n    );\n    buttongrp.appendChild(button);\n    var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n    titlebar.insertBefore(buttongrp, titlebar.firstChild);\n};\n\nmpl.figure.prototype._remove_fig_handler = function (event) {\n    var fig = event.data.fig;\n    if (event.target !== this) {\n        // Ignore bubbled events from children.\n        return;\n    }\n    fig.close_ws(fig, {});\n};\n\nmpl.figure.prototype._root_extra_style = function (el) {\n    el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n};\n\nmpl.figure.prototype._canvas_extra_style = function (el) {\n    // this is important to make the div 'focusable\n    el.setAttribute('tabindex', 0);\n    // reach out to IPython and tell the keyboard manager to turn it's self\n    // off when our div gets focus\n\n    // location in version 3\n    if (IPython.notebook.keyboard_manager) {\n        IPython.notebook.keyboard_manager.register_events(el);\n    } else {\n        // location in version 2\n        IPython.keyboard_manager.register_events(el);\n    }\n};\n\nmpl.figure.prototype._key_event_extra = function (event, _name) {\n    // Check for shift+enter\n    if (event.shiftKey && event.which === 13) {\n        this.canvas_div.blur();\n        // select the cell after this one\n        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n        IPython.notebook.select(index + 1);\n    }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n    fig.ondownload(fig, null);\n};\n\nmpl.find_output_cell = function (html_output) {\n    // Return the cell and output element which can be found *uniquely* in the notebook.\n    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n    // IPython event is triggered only after the cells have been serialised, which for\n    // our purposes (turning an active figure into a static one), is too late.\n    var cells = IPython.notebook.get_cells();\n    var ncells = cells.length;\n    for (var i = 0; i < ncells; i++) {\n        var cell = cells[i];\n        if (cell.cell_type === 'code') {\n            for (var j = 0; j < cell.output_area.outputs.length; j++) {\n                var data = cell.output_area.outputs[j];\n                if (data.data) {\n                    // IPython >= 3 moved mimebundle to data attribute of output\n                    data = data.data;\n                }\n                if (data['text/html'] === html_output) {\n                    return [cell, data, j];\n                }\n            }\n        }\n    }\n};\n\n// Register the function which deals with the matplotlib target/channel.\n// The kernel may be null if the page has been refreshed.\nif (IPython.notebook.kernel !== null) {\n    IPython.notebook.kernel.comm_manager.register_target(\n        'matplotlib',\n        mpl.mpl_figure_comm\n    );\n}\n",
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div id='c85034de-f394-4345-94c0-0231f6f279d0'></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "cerebro.plot(style='candlestick')\n",
    "fig = cerebro.plot(style='candlestick')\n",
    "\n",
    "# 显示图片\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "nbagg\n"
     ]
    }
   ],
   "source": [
    "import matplotlib\n",
    "print(matplotlib.get_backend())\n",
    "\n",
    "# 切换到交互式后端\n",
    "matplotlib.use('TkAgg')  # 或其他交互式后端，如 'Qt5Agg'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": "/* Put everything inside the global mpl namespace */\n/* global mpl */\nwindow.mpl = {};\n\nmpl.get_websocket_type = function () {\n    if (typeof WebSocket !== 'undefined') {\n        return WebSocket;\n    } else if (typeof MozWebSocket !== 'undefined') {\n        return MozWebSocket;\n    } else {\n        alert(\n            'Your browser does not have WebSocket support. ' +\n                'Please try Chrome, Safari or Firefox ≥ 6. ' +\n                'Firefox 4 and 5 are also supported but you ' +\n                'have to enable WebSockets in about:config.'\n        );\n    }\n};\n\nmpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n    this.id = figure_id;\n\n    this.ws = websocket;\n\n    this.supports_binary = this.ws.binaryType !== undefined;\n\n    if (!this.supports_binary) {\n        var warnings = document.getElementById('mpl-warnings');\n        if (warnings) {\n            warnings.style.display = 'block';\n            warnings.textContent =\n                'This browser does not support binary websocket messages. ' +\n                'Performance may be slow.';\n        }\n    }\n\n    this.imageObj = new Image();\n\n    this.context = undefined;\n    this.message = undefined;\n    this.canvas = undefined;\n    this.rubberband_canvas = undefined;\n    this.rubberband_context = undefined;\n    this.format_dropdown = undefined;\n\n    this.image_mode = 'full';\n\n    this.root = document.createElement('div');\n    this.root.setAttribute('style', 'display: inline-block');\n    this._root_extra_style(this.root);\n\n    parent_element.appendChild(this.root);\n\n    this._init_header(this);\n    this._init_canvas(this);\n    this._init_toolbar(this);\n\n    var fig = this;\n\n    this.waiting = false;\n\n    this.ws.onopen = function () {\n        fig.send_message('supports_binary', { value: fig.supports_binary });\n        fig.send_message('send_image_mode', {});\n        if (fig.ratio !== 1) {\n            fig.send_message('set_device_pixel_ratio', {\n                device_pixel_ratio: fig.ratio,\n            });\n        }\n        fig.send_message('refresh', {});\n    };\n\n    this.imageObj.onload = function () {\n        if (fig.image_mode === 'full') {\n            // Full images could contain transparency (where diff images\n            // almost always do), so we need to clear the canvas so that\n            // there is no ghosting.\n            fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n        }\n        fig.context.drawImage(fig.imageObj, 0, 0);\n    };\n\n    this.imageObj.onunload = function () {\n        fig.ws.close();\n    };\n\n    this.ws.onmessage = this._make_on_message_function(this);\n\n    this.ondownload = ondownload;\n};\n\nmpl.figure.prototype._init_header = function () {\n    var titlebar = document.createElement('div');\n    titlebar.classList =\n        'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n    var titletext = document.createElement('div');\n    titletext.classList = 'ui-dialog-title';\n    titletext.setAttribute(\n        'style',\n        'width: 100%; text-align: center; padding: 3px;'\n    );\n    titlebar.appendChild(titletext);\n    this.root.appendChild(titlebar);\n    this.header = titletext;\n};\n\nmpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._init_canvas = function () {\n    var fig = this;\n\n    var canvas_div = (this.canvas_div = document.createElement('div'));\n    canvas_div.setAttribute('tabindex', '0');\n    canvas_div.setAttribute(\n        'style',\n        'border: 1px solid #ddd;' +\n            'box-sizing: content-box;' +\n            'clear: both;' +\n            'min-height: 1px;' +\n            'min-width: 1px;' +\n            'outline: 0;' +\n            'overflow: hidden;' +\n            'position: relative;' +\n            'resize: both;' +\n            'z-index: 2;'\n    );\n\n    function on_keyboard_event_closure(name) {\n        return function (event) {\n            return fig.key_event(event, name);\n        };\n    }\n\n    canvas_div.addEventListener(\n        'keydown',\n        on_keyboard_event_closure('key_press')\n    );\n    canvas_div.addEventListener(\n        'keyup',\n        on_keyboard_event_closure('key_release')\n    );\n\n    this._canvas_extra_style(canvas_div);\n    this.root.appendChild(canvas_div);\n\n    var canvas = (this.canvas = document.createElement('canvas'));\n    canvas.classList.add('mpl-canvas');\n    canvas.setAttribute(\n        'style',\n        'box-sizing: content-box;' +\n            'pointer-events: none;' +\n            'position: relative;' +\n            'z-index: 0;'\n    );\n\n    this.context = canvas.getContext('2d');\n\n    var backingStore =\n        this.context.backingStorePixelRatio ||\n        this.context.webkitBackingStorePixelRatio ||\n        this.context.mozBackingStorePixelRatio ||\n        this.context.msBackingStorePixelRatio ||\n        this.context.oBackingStorePixelRatio ||\n        this.context.backingStorePixelRatio ||\n        1;\n\n    this.ratio = (window.devicePixelRatio || 1) / backingStore;\n\n    var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n        'canvas'\n    ));\n    rubberband_canvas.setAttribute(\n        'style',\n        'box-sizing: content-box;' +\n            'left: 0;' +\n            'pointer-events: none;' +\n            'position: absolute;' +\n            'top: 0;' +\n            'z-index: 1;'\n    );\n\n    // Apply a ponyfill if ResizeObserver is not implemented by browser.\n    if (this.ResizeObserver === undefined) {\n        if (window.ResizeObserver !== undefined) {\n            this.ResizeObserver = window.ResizeObserver;\n        } else {\n            var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n            this.ResizeObserver = obs.ResizeObserver;\n        }\n    }\n\n    this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n        // There's no need to resize if the WebSocket is not connected:\n        // - If it is still connecting, then we will get an initial resize from\n        //   Python once it connects.\n        // - If it has disconnected, then resizing will clear the canvas and\n        //   never get anything back to refill it, so better to not resize and\n        //   keep something visible.\n        if (fig.ws.readyState != 1) {\n            return;\n        }\n        var nentries = entries.length;\n        for (var i = 0; i < nentries; i++) {\n            var entry = entries[i];\n            var width, height;\n            if (entry.contentBoxSize) {\n                if (entry.contentBoxSize instanceof Array) {\n                    // Chrome 84 implements new version of spec.\n                    width = entry.contentBoxSize[0].inlineSize;\n                    height = entry.contentBoxSize[0].blockSize;\n                } else {\n                    // Firefox implements old version of spec.\n                    width = entry.contentBoxSize.inlineSize;\n                    height = entry.contentBoxSize.blockSize;\n                }\n            } else {\n                // Chrome <84 implements even older version of spec.\n                width = entry.contentRect.width;\n                height = entry.contentRect.height;\n            }\n\n            // Keep the size of the canvas and rubber band canvas in sync with\n            // the canvas container.\n            if (entry.devicePixelContentBoxSize) {\n                // Chrome 84 implements new version of spec.\n                canvas.setAttribute(\n                    'width',\n                    entry.devicePixelContentBoxSize[0].inlineSize\n                );\n                canvas.setAttribute(\n                    'height',\n                    entry.devicePixelContentBoxSize[0].blockSize\n                );\n            } else {\n                canvas.setAttribute('width', width * fig.ratio);\n                canvas.setAttribute('height', height * fig.ratio);\n            }\n            /* This rescales the canvas back to display pixels, so that it\n             * appears correct on HiDPI screens. */\n            canvas.style.width = width + 'px';\n            canvas.style.height = height + 'px';\n\n            rubberband_canvas.setAttribute('width', width);\n            rubberband_canvas.setAttribute('height', height);\n\n            // And update the size in Python. We ignore the initial 0/0 size\n            // that occurs as the element is placed into the DOM, which should\n            // otherwise not happen due to the minimum size styling.\n            if (width != 0 && height != 0) {\n                fig.request_resize(width, height);\n            }\n        }\n    });\n    this.resizeObserverInstance.observe(canvas_div);\n\n    function on_mouse_event_closure(name) {\n        /* User Agent sniffing is bad, but WebKit is busted:\n         * https://bugs.webkit.org/show_bug.cgi?id=144526\n         * https://bugs.webkit.org/show_bug.cgi?id=181818\n         * The worst that happens here is that they get an extra browser\n         * selection when dragging, if this check fails to catch them.\n         */\n        var UA = navigator.userAgent;\n        var isWebKit = /AppleWebKit/.test(UA) && !/Chrome/.test(UA);\n        if(isWebKit) {\n            return function (event) {\n                /* This prevents the web browser from automatically changing to\n                 * the text insertion cursor when the button is pressed. We\n                 * want to control all of the cursor setting manually through\n                 * the 'cursor' event from matplotlib */\n                event.preventDefault()\n                return fig.mouse_event(event, name);\n            };\n        } else {\n            return function (event) {\n                return fig.mouse_event(event, name);\n            };\n        }\n    }\n\n    canvas_div.addEventListener(\n        'mousedown',\n        on_mouse_event_closure('button_press')\n    );\n    canvas_div.addEventListener(\n        'mouseup',\n        on_mouse_event_closure('button_release')\n    );\n    canvas_div.addEventListener(\n        'dblclick',\n        on_mouse_event_closure('dblclick')\n    );\n    // Throttle sequential mouse events to 1 every 20ms.\n    canvas_div.addEventListener(\n        'mousemove',\n        on_mouse_event_closure('motion_notify')\n    );\n\n    canvas_div.addEventListener(\n        'mouseenter',\n        on_mouse_event_closure('figure_enter')\n    );\n    canvas_div.addEventListener(\n        'mouseleave',\n        on_mouse_event_closure('figure_leave')\n    );\n\n    canvas_div.addEventListener('wheel', function (event) {\n        if (event.deltaY < 0) {\n            event.step = 1;\n        } else {\n            event.step = -1;\n        }\n        on_mouse_event_closure('scroll')(event);\n    });\n\n    canvas_div.appendChild(canvas);\n    canvas_div.appendChild(rubberband_canvas);\n\n    this.rubberband_context = rubberband_canvas.getContext('2d');\n    this.rubberband_context.strokeStyle = '#000000';\n\n    this._resize_canvas = function (width, height, forward) {\n        if (forward) {\n            canvas_div.style.width = width + 'px';\n            canvas_div.style.height = height + 'px';\n        }\n    };\n\n    // Disable right mouse context menu.\n    canvas_div.addEventListener('contextmenu', function (_e) {\n        event.preventDefault();\n        return false;\n    });\n\n    function set_focus() {\n        canvas.focus();\n        canvas_div.focus();\n    }\n\n    window.setTimeout(set_focus, 100);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n    var fig = this;\n\n    var toolbar = document.createElement('div');\n    toolbar.classList = 'mpl-toolbar';\n    this.root.appendChild(toolbar);\n\n    function on_click_closure(name) {\n        return function (_event) {\n            return fig.toolbar_button_onclick(name);\n        };\n    }\n\n    function on_mouseover_closure(tooltip) {\n        return function (event) {\n            if (!event.currentTarget.disabled) {\n                return fig.toolbar_button_onmouseover(tooltip);\n            }\n        };\n    }\n\n    fig.buttons = {};\n    var buttonGroup = document.createElement('div');\n    buttonGroup.classList = 'mpl-button-group';\n    for (var toolbar_ind in mpl.toolbar_items) {\n        var name = mpl.toolbar_items[toolbar_ind][0];\n        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n        var image = mpl.toolbar_items[toolbar_ind][2];\n        var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n        if (!name) {\n            /* Instead of a spacer, we start a new button group. */\n            if (buttonGroup.hasChildNodes()) {\n                toolbar.appendChild(buttonGroup);\n            }\n            buttonGroup = document.createElement('div');\n            buttonGroup.classList = 'mpl-button-group';\n            continue;\n        }\n\n        var button = (fig.buttons[name] = document.createElement('button'));\n        button.classList = 'mpl-widget';\n        button.setAttribute('role', 'button');\n        button.setAttribute('aria-disabled', 'false');\n        button.addEventListener('click', on_click_closure(method_name));\n        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n\n        var icon_img = document.createElement('img');\n        icon_img.src = '_images/' + image + '.png';\n        icon_img.srcset = '_images/' + image + '_large.png 2x';\n        icon_img.alt = tooltip;\n        button.appendChild(icon_img);\n\n        buttonGroup.appendChild(button);\n    }\n\n    if (buttonGroup.hasChildNodes()) {\n        toolbar.appendChild(buttonGroup);\n    }\n\n    var fmt_picker = document.createElement('select');\n    fmt_picker.classList = 'mpl-widget';\n    toolbar.appendChild(fmt_picker);\n    this.format_dropdown = fmt_picker;\n\n    for (var ind in mpl.extensions) {\n        var fmt = mpl.extensions[ind];\n        var option = document.createElement('option');\n        option.selected = fmt === mpl.default_extension;\n        option.innerHTML = fmt;\n        fmt_picker.appendChild(option);\n    }\n\n    var status_bar = document.createElement('span');\n    status_bar.classList = 'mpl-message';\n    toolbar.appendChild(status_bar);\n    this.message = status_bar;\n};\n\nmpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n    // which will in turn request a refresh of the image.\n    this.send_message('resize', { width: x_pixels, height: y_pixels });\n};\n\nmpl.figure.prototype.send_message = function (type, properties) {\n    properties['type'] = type;\n    properties['figure_id'] = this.id;\n    this.ws.send(JSON.stringify(properties));\n};\n\nmpl.figure.prototype.send_draw_message = function () {\n    if (!this.waiting) {\n        this.waiting = true;\n        this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n    }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n    var format_dropdown = fig.format_dropdown;\n    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n    fig.ondownload(fig, format);\n};\n\nmpl.figure.prototype.handle_resize = function (fig, msg) {\n    var size = msg['size'];\n    if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n        fig._resize_canvas(size[0], size[1], msg['forward']);\n        fig.send_message('refresh', {});\n    }\n};\n\nmpl.figure.prototype.handle_rubberband = function (fig, msg) {\n    var x0 = msg['x0'] / fig.ratio;\n    var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n    var x1 = msg['x1'] / fig.ratio;\n    var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n    x0 = Math.floor(x0) + 0.5;\n    y0 = Math.floor(y0) + 0.5;\n    x1 = Math.floor(x1) + 0.5;\n    y1 = Math.floor(y1) + 0.5;\n    var min_x = Math.min(x0, x1);\n    var min_y = Math.min(y0, y1);\n    var width = Math.abs(x1 - x0);\n    var height = Math.abs(y1 - y0);\n\n    fig.rubberband_context.clearRect(\n        0,\n        0,\n        fig.canvas.width / fig.ratio,\n        fig.canvas.height / fig.ratio\n    );\n\n    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n};\n\nmpl.figure.prototype.handle_figure_label = function (fig, msg) {\n    // Updates the figure title.\n    fig.header.textContent = msg['label'];\n};\n\nmpl.figure.prototype.handle_cursor = function (fig, msg) {\n    fig.canvas_div.style.cursor = msg['cursor'];\n};\n\nmpl.figure.prototype.handle_message = function (fig, msg) {\n    fig.message.textContent = msg['message'];\n};\n\nmpl.figure.prototype.handle_draw = function (fig, _msg) {\n    // Request the server to send over a new figure.\n    fig.send_draw_message();\n};\n\nmpl.figure.prototype.handle_image_mode = function (fig, msg) {\n    fig.image_mode = msg['mode'];\n};\n\nmpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n    for (var key in msg) {\n        if (!(key in fig.buttons)) {\n            continue;\n        }\n        fig.buttons[key].disabled = !msg[key];\n        fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n    }\n};\n\nmpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n    if (msg['mode'] === 'PAN') {\n        fig.buttons['Pan'].classList.add('active');\n        fig.buttons['Zoom'].classList.remove('active');\n    } else if (msg['mode'] === 'ZOOM') {\n        fig.buttons['Pan'].classList.remove('active');\n        fig.buttons['Zoom'].classList.add('active');\n    } else {\n        fig.buttons['Pan'].classList.remove('active');\n        fig.buttons['Zoom'].classList.remove('active');\n    }\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n    // Called whenever the canvas gets updated.\n    this.send_message('ack', {});\n};\n\n// A function to construct a web socket function for onmessage handling.\n// Called in the figure constructor.\nmpl.figure.prototype._make_on_message_function = function (fig) {\n    return function socket_on_message(evt) {\n        if (evt.data instanceof Blob) {\n            var img = evt.data;\n            if (img.type !== 'image/png') {\n                /* FIXME: We get \"Resource interpreted as Image but\n                 * transferred with MIME type text/plain:\" errors on\n                 * Chrome.  But how to set the MIME type?  It doesn't seem\n                 * to be part of the websocket stream */\n                img.type = 'image/png';\n            }\n\n            /* Free the memory for the previous frames */\n            if (fig.imageObj.src) {\n                (window.URL || window.webkitURL).revokeObjectURL(\n                    fig.imageObj.src\n                );\n            }\n\n            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n                img\n            );\n            fig.updated_canvas_event();\n            fig.waiting = false;\n            return;\n        } else if (\n            typeof evt.data === 'string' &&\n            evt.data.slice(0, 21) === 'data:image/png;base64'\n        ) {\n            fig.imageObj.src = evt.data;\n            fig.updated_canvas_event();\n            fig.waiting = false;\n            return;\n        }\n\n        var msg = JSON.parse(evt.data);\n        var msg_type = msg['type'];\n\n        // Call the  \"handle_{type}\" callback, which takes\n        // the figure and JSON message as its only arguments.\n        try {\n            var callback = fig['handle_' + msg_type];\n        } catch (e) {\n            console.log(\n                \"No handler for the '\" + msg_type + \"' message type: \",\n                msg\n            );\n            return;\n        }\n\n        if (callback) {\n            try {\n                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n                callback(fig, msg);\n            } catch (e) {\n                console.log(\n                    \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n                    e,\n                    e.stack,\n                    msg\n                );\n            }\n        }\n    };\n};\n\nfunction getModifiers(event) {\n    var mods = [];\n    if (event.ctrlKey) {\n        mods.push('ctrl');\n    }\n    if (event.altKey) {\n        mods.push('alt');\n    }\n    if (event.shiftKey) {\n        mods.push('shift');\n    }\n    if (event.metaKey) {\n        mods.push('meta');\n    }\n    return mods;\n}\n\n/*\n * return a copy of an object with only non-object keys\n * we need this to avoid circular references\n * https://stackoverflow.com/a/24161582/3208463\n */\nfunction simpleKeys(original) {\n    return Object.keys(original).reduce(function (obj, key) {\n        if (typeof original[key] !== 'object') {\n            obj[key] = original[key];\n        }\n        return obj;\n    }, {});\n}\n\nmpl.figure.prototype.mouse_event = function (event, name) {\n    if (name === 'button_press') {\n        this.canvas.focus();\n        this.canvas_div.focus();\n    }\n\n    // from https://stackoverflow.com/q/1114465\n    var boundingRect = this.canvas.getBoundingClientRect();\n    var x = (event.clientX - boundingRect.left) * this.ratio;\n    var y = (event.clientY - boundingRect.top) * this.ratio;\n\n    this.send_message(name, {\n        x: x,\n        y: y,\n        button: event.button,\n        step: event.step,\n        modifiers: getModifiers(event),\n        guiEvent: simpleKeys(event),\n    });\n\n    return false;\n};\n\nmpl.figure.prototype._key_event_extra = function (_event, _name) {\n    // Handle any extra behaviour associated with a key event\n};\n\nmpl.figure.prototype.key_event = function (event, name) {\n    // Prevent repeat events\n    if (name === 'key_press') {\n        if (event.key === this._key) {\n            return;\n        } else {\n            this._key = event.key;\n        }\n    }\n    if (name === 'key_release') {\n        this._key = null;\n    }\n\n    var value = '';\n    if (event.ctrlKey && event.key !== 'Control') {\n        value += 'ctrl+';\n    }\n    else if (event.altKey && event.key !== 'Alt') {\n        value += 'alt+';\n    }\n    else if (event.shiftKey && event.key !== 'Shift') {\n        value += 'shift+';\n    }\n\n    value += 'k' + event.key;\n\n    this._key_event_extra(event, name);\n\n    this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n    return false;\n};\n\nmpl.figure.prototype.toolbar_button_onclick = function (name) {\n    if (name === 'download') {\n        this.handle_save(this, null);\n    } else {\n        this.send_message('toolbar_button', { name: name });\n    }\n};\n\nmpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n    this.message.textContent = tooltip;\n};\n\n///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n// prettier-ignore\nvar _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\nmpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\", \"webp\"];\n\nmpl.default_extension = \"png\";/* global mpl */\n\nvar comm_websocket_adapter = function (comm) {\n    // Create a \"websocket\"-like object which calls the given IPython comm\n    // object with the appropriate methods. Currently this is a non binary\n    // socket, so there is still some room for performance tuning.\n    var ws = {};\n\n    ws.binaryType = comm.kernel.ws.binaryType;\n    ws.readyState = comm.kernel.ws.readyState;\n    function updateReadyState(_event) {\n        if (comm.kernel.ws) {\n            ws.readyState = comm.kernel.ws.readyState;\n        } else {\n            ws.readyState = 3; // Closed state.\n        }\n    }\n    comm.kernel.ws.addEventListener('open', updateReadyState);\n    comm.kernel.ws.addEventListener('close', updateReadyState);\n    comm.kernel.ws.addEventListener('error', updateReadyState);\n\n    ws.close = function () {\n        comm.close();\n    };\n    ws.send = function (m) {\n        //console.log('sending', m);\n        comm.send(m);\n    };\n    // Register the callback with on_msg.\n    comm.on_msg(function (msg) {\n        //console.log('receiving', msg['content']['data'], msg);\n        var data = msg['content']['data'];\n        if (data['blob'] !== undefined) {\n            data = {\n                data: new Blob(msg['buffers'], { type: data['blob'] }),\n            };\n        }\n        // Pass the mpl event to the overridden (by mpl) onmessage function.\n        ws.onmessage(data);\n    });\n    return ws;\n};\n\nmpl.mpl_figure_comm = function (comm, msg) {\n    // This is the function which gets called when the mpl process\n    // starts-up an IPython Comm through the \"matplotlib\" channel.\n\n    var id = msg.content.data.id;\n    // Get hold of the div created by the display call when the Comm\n    // socket was opened in Python.\n    var element = document.getElementById(id);\n    var ws_proxy = comm_websocket_adapter(comm);\n\n    function ondownload(figure, _format) {\n        window.open(figure.canvas.toDataURL());\n    }\n\n    var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n\n    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n    // web socket which is closed, not our websocket->open comm proxy.\n    ws_proxy.onopen();\n\n    fig.parent_element = element;\n    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n    if (!fig.cell_info) {\n        console.error('Failed to find cell for figure', id, fig);\n        return;\n    }\n    fig.cell_info[0].output_area.element.on(\n        'cleared',\n        { fig: fig },\n        fig._remove_fig_handler\n    );\n};\n\nmpl.figure.prototype.handle_close = function (fig, msg) {\n    var width = fig.canvas.width / fig.ratio;\n    fig.cell_info[0].output_area.element.off(\n        'cleared',\n        fig._remove_fig_handler\n    );\n    fig.resizeObserverInstance.unobserve(fig.canvas_div);\n\n    // Update the output cell to use the data from the current canvas.\n    fig.push_to_output();\n    var dataURL = fig.canvas.toDataURL();\n    // Re-enable the keyboard manager in IPython - without this line, in FF,\n    // the notebook keyboard shortcuts fail.\n    IPython.keyboard_manager.enable();\n    fig.parent_element.innerHTML =\n        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n    fig.close_ws(fig, msg);\n};\n\nmpl.figure.prototype.close_ws = function (fig, msg) {\n    fig.send_message('closing', msg);\n    // fig.ws.close()\n};\n\nmpl.figure.prototype.push_to_output = function (_remove_interactive) {\n    // Turn the data on the canvas into data in the output cell.\n    var width = this.canvas.width / this.ratio;\n    var dataURL = this.canvas.toDataURL();\n    this.cell_info[1]['text/html'] =\n        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n    // Tell IPython that the notebook contents must change.\n    IPython.notebook.set_dirty(true);\n    this.send_message('ack', {});\n    var fig = this;\n    // Wait a second, then push the new image to the DOM so\n    // that it is saved nicely (might be nice to debounce this).\n    setTimeout(function () {\n        fig.push_to_output();\n    }, 1000);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n    var fig = this;\n\n    var toolbar = document.createElement('div');\n    toolbar.classList = 'btn-toolbar';\n    this.root.appendChild(toolbar);\n\n    function on_click_closure(name) {\n        return function (_event) {\n            return fig.toolbar_button_onclick(name);\n        };\n    }\n\n    function on_mouseover_closure(tooltip) {\n        return function (event) {\n            if (!event.currentTarget.disabled) {\n                return fig.toolbar_button_onmouseover(tooltip);\n            }\n        };\n    }\n\n    fig.buttons = {};\n    var buttonGroup = document.createElement('div');\n    buttonGroup.classList = 'btn-group';\n    var button;\n    for (var toolbar_ind in mpl.toolbar_items) {\n        var name = mpl.toolbar_items[toolbar_ind][0];\n        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n        var image = mpl.toolbar_items[toolbar_ind][2];\n        var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n        if (!name) {\n            /* Instead of a spacer, we start a new button group. */\n            if (buttonGroup.hasChildNodes()) {\n                toolbar.appendChild(buttonGroup);\n            }\n            buttonGroup = document.createElement('div');\n            buttonGroup.classList = 'btn-group';\n            continue;\n        }\n\n        button = fig.buttons[name] = document.createElement('button');\n        button.classList = 'btn btn-default';\n        button.href = '#';\n        button.title = name;\n        button.innerHTML = '<i class=\"fa ' + image + ' fa-lg\"></i>';\n        button.addEventListener('click', on_click_closure(method_name));\n        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n        buttonGroup.appendChild(button);\n    }\n\n    if (buttonGroup.hasChildNodes()) {\n        toolbar.appendChild(buttonGroup);\n    }\n\n    // Add the status bar.\n    var status_bar = document.createElement('span');\n    status_bar.classList = 'mpl-message pull-right';\n    toolbar.appendChild(status_bar);\n    this.message = status_bar;\n\n    // Add the close button to the window.\n    var buttongrp = document.createElement('div');\n    buttongrp.classList = 'btn-group inline pull-right';\n    button = document.createElement('button');\n    button.classList = 'btn btn-mini btn-primary';\n    button.href = '#';\n    button.title = 'Stop Interaction';\n    button.innerHTML = '<i class=\"fa fa-power-off icon-remove icon-large\"></i>';\n    button.addEventListener('click', function (_evt) {\n        fig.handle_close(fig, {});\n    });\n    button.addEventListener(\n        'mouseover',\n        on_mouseover_closure('Stop Interaction')\n    );\n    buttongrp.appendChild(button);\n    var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n    titlebar.insertBefore(buttongrp, titlebar.firstChild);\n};\n\nmpl.figure.prototype._remove_fig_handler = function (event) {\n    var fig = event.data.fig;\n    if (event.target !== this) {\n        // Ignore bubbled events from children.\n        return;\n    }\n    fig.close_ws(fig, {});\n};\n\nmpl.figure.prototype._root_extra_style = function (el) {\n    el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n};\n\nmpl.figure.prototype._canvas_extra_style = function (el) {\n    // this is important to make the div 'focusable\n    el.setAttribute('tabindex', 0);\n    // reach out to IPython and tell the keyboard manager to turn it's self\n    // off when our div gets focus\n\n    // location in version 3\n    if (IPython.notebook.keyboard_manager) {\n        IPython.notebook.keyboard_manager.register_events(el);\n    } else {\n        // location in version 2\n        IPython.keyboard_manager.register_events(el);\n    }\n};\n\nmpl.figure.prototype._key_event_extra = function (event, _name) {\n    // Check for shift+enter\n    if (event.shiftKey && event.which === 13) {\n        this.canvas_div.blur();\n        // select the cell after this one\n        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n        IPython.notebook.select(index + 1);\n    }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n    fig.ondownload(fig, null);\n};\n\nmpl.find_output_cell = function (html_output) {\n    // Return the cell and output element which can be found *uniquely* in the notebook.\n    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n    // IPython event is triggered only after the cells have been serialised, which for\n    // our purposes (turning an active figure into a static one), is too late.\n    var cells = IPython.notebook.get_cells();\n    var ncells = cells.length;\n    for (var i = 0; i < ncells; i++) {\n        var cell = cells[i];\n        if (cell.cell_type === 'code') {\n            for (var j = 0; j < cell.output_area.outputs.length; j++) {\n                var data = cell.output_area.outputs[j];\n                if (data.data) {\n                    // IPython >= 3 moved mimebundle to data attribute of output\n                    data = data.data;\n                }\n                if (data['text/html'] === html_output) {\n                    return [cell, data, j];\n                }\n            }\n        }\n    }\n};\n\n// Register the function which deals with the matplotlib target/channel.\n// The kernel may be null if the page has been refreshed.\nif (IPython.notebook.kernel !== null) {\n    IPython.notebook.kernel.comm_manager.register_target(\n        'matplotlib',\n        mpl.mpl_figure_comm\n    );\n}\n",
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div id='cc29933f-9fe4-49c3-a187-b4e6481da2f4'></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "[[<Figure size 640x480 with 4 Axes>]]"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cerebro.plot(style='candlestick')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "    # modpath = os.path.dirname(os.path.abspath(sys.argv[0]))\n",
    "    modpath = 'C:\\\\Users\\\\bww\\gitee\\\\stock\\\\backtrader'\n",
    "    datapath = os.path.join(modpath, '000001.SS.csv')\n",
    "    \n",
    "    datapath='E:\\stock\\stock\\backtrader\\000001.SS.csv'"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
